DPDK patches and discussions
 help / color / mirror / Atom feed
* [PATCH v1 0/2] Add config file support for l3fwd
@ 2021-12-16 15:01 Sean Morrissey
  2021-12-16 15:01 ` [PATCH v1 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
                   ` (2 more replies)
  0 siblings, 3 replies; 46+ messages in thread
From: Sean Morrissey @ 2021-12-16 15:01 UTC (permalink / raw)
  Cc: dev, Sean Morrissey

This patchset introduces config file support for l3fwd
and its lookup methods LPM, FIB, and EM, similar to
that of l3fwd-acl. This allows for route rules to be
defined in configuration files and edited there instead
of in each of the lookup methods hardcoded route tables.

Sean Morrissey (2):
  examples/l3fwd: add config file support for LPM/FIB
  examples/l3fwd: add config file support for EM

 doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
 examples/l3fwd/em_default_v4.cfg        |  17 +
 examples/l3fwd/em_default_v6.cfg        |  17 +
 examples/l3fwd/l3fwd.h                  |  35 ++
 examples/l3fwd/l3fwd_em.c               | 478 ++++++++++++++----------
 examples/l3fwd/l3fwd_fib.c              |  52 +--
 examples/l3fwd/l3fwd_lpm.c              | 281 +++++++++++++-
 examples/l3fwd/l3fwd_route.h            |  49 ++-
 examples/l3fwd/lpm_default_v4.cfg       |  17 +
 examples/l3fwd/lpm_default_v6.cfg       |  17 +
 examples/l3fwd/main.c                   |  97 ++---
 11 files changed, 845 insertions(+), 304 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg

-- 
2.25.1


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

* [PATCH v1 1/2] examples/l3fwd: add config file support for LPM/FIB
  2021-12-16 15:01 [PATCH v1 0/2] Add config file support for l3fwd Sean Morrissey
@ 2021-12-16 15:01 ` Sean Morrissey
  2021-12-16 15:01 ` [PATCH v1 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
  2021-12-20 11:08 ` [PATCH v2 0/2] Add config file support for l3fwd Sean Morrissey
  2 siblings, 0 replies; 46+ messages in thread
From: Sean Morrissey @ 2021-12-16 15:01 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Ravi Kerur

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for LPM and FIB,
with format similar to l3fwd-acl one.

With the removal of the hardcoded route tables for IPv4
and IPv6, these routes have been moved to a separate
default config file for use with LPM and FIB.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd/l3fwd.h            |  35 ++++
 examples/l3fwd/l3fwd_em.c         |   7 +
 examples/l3fwd/l3fwd_fib.c        |  52 +++---
 examples/l3fwd/l3fwd_lpm.c        | 281 +++++++++++++++++++++++++++---
 examples/l3fwd/l3fwd_route.h      |  17 +-
 examples/l3fwd/lpm_default_v4.cfg |  17 ++
 examples/l3fwd/lpm_default_v6.cfg |  17 ++
 examples/l3fwd/main.c             |  97 ++++++-----
 8 files changed, 437 insertions(+), 86 deletions(-)
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg

diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 38ca19133c..d8b1f971e1 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -58,6 +58,30 @@
 #endif
 #define HASH_ENTRY_NUMBER_DEFAULT	16
 
+/*Log file related character defs. */
+#define COMMENT_LEAD_CHAR	('#')
+#define ROUTE_LEAD_CHAR		('R')
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+struct parm_cfg {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+};
+
 struct mbuf_table {
 	uint16_t len;
 	struct rte_mbuf *m_table[MAX_PKT_BURST];
@@ -96,6 +120,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern struct parm_cfg parm_config;
+
 /* Send burst of packets on an output interface */
 static inline int
 send_burst(struct lcore_conf *qconf, uint16_t n, uint16_t port)
@@ -183,6 +209,12 @@ int
 init_mem(uint16_t portid, unsigned int nb_mbuf);
 
 /* Function pointers for LPM, EM or FIB functionality. */
+void
+read_config_files_lpm(void);
+
+void
+read_config_files_em(void);
+
 void
 setup_lpm(const int socketid);
 
@@ -286,4 +318,7 @@ fib_get_ipv4_l3fwd_lookup_struct(const int socketid);
 void *
 fib_get_ipv6_l3fwd_lookup_struct(const int socketid);
 
+int
+is_bypass_line(const char *buff);
+
 #endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 5cc4a4d979..4953cdae4e 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -972,6 +972,13 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+/* Load rules from the input file */
+void
+read_config_files_em(void)
+{
+	/* Empty till config file support added to EM */
+}
+
 /* Initialize exact match (hash) parameters. 8< */
 void
 setup_hash(const int socketid)
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 2110459cc3..003721c908 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -583,7 +583,7 @@ setup_fib(const int socketid)
 	struct rte_eth_dev_info dev_info;
 	struct rte_fib6_conf config;
 	struct rte_fib_conf config_ipv4;
-	unsigned int i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -603,37 +603,39 @@ setup_fib(const int socketid)
 			"Unable to create the l3fwd FIB table on socket %d\n",
 			socketid);
 
+
 	/* Populate the fib ipv4 table. */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* Skip unused ports. */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_fib_add(ipv4_l3fwd_fib_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		if (inet_ntop(AF_INET, &in, abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv4_l3fwd_route_array[i].depth,
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].depth,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv4 route added to port %d [%s]\n",
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		}
 	}
@@ -650,44 +652,50 @@ setup_fib(const int socketid)
 	config.trie.num_tbl8 = (1 << 15);
 	ipv6_l3fwd_fib_lookup_struct[socketid] = rte_fib6_create(s, socketid,
 			&config);
-	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL) {
+		free(route_base_v4);
 		rte_exit(EXIT_FAILURE,
 				"Unable to create the l3fwd FIB table on socket %d\n",
 				socketid);
+	}
 
 	/* Populate the fib IPv6 table. */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* Skip unused ports. */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv6_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_fib6_add(ipv6_l3fwd_fib_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
+			free(route_base_v6);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		if (inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip,
+		if (inet_ntop(AF_INET6, route_base_v6[i].ip_8,
 				abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv6_l3fwd_route_array[i].depth,
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].depth,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv6 route added to port %d [%s]\n",
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		}
 	}
+	free(route_base_v4);
+	free(route_base_v6);
 }
 
 /* Return ipv4 fib lookup struct. */
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index a5b476ced3..4157f16c07 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -39,6 +39,16 @@
 
 static struct rte_lpm *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 static struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
+struct lpm_route_rule *route_base_v4;
+struct lpm_route_rule *route_base_v6;
+int route_num_v4;
+int route_num_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
 
 /* Performing LPM-based lookups. 8< */
 static inline uint16_t
@@ -139,6 +149,199 @@ lpm_get_dst_port_with_ipv4(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
 #include "l3fwd_lpm.h"
 #endif
 
+static int
+parse_ipv6_addr_mask(char *token, uint32_t *ipv6, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET6, sa, ipv6) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 128, 0);
+	return 0;
+}
+
+static int
+parse_ipv4_addr_mask(char *token, uint32_t *ipv4, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET, sa, ipv4) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 32, 0);
+	*ipv4 = ntohl(*ipv4);
+	return 0;
+}
+
+static int
+lpm_parse_v6_net(char *in, uint32_t *v, uint8_t *mask_len)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = parse_ipv6_addr_mask(in, v, mask_len);
+
+	return rc;
+}
+
+static int
+lpm_parse_v6_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], v->ip_32, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_parse_v4_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = parse_ipv4_addr_mask(in[CB_FLD_DST_ADDR], &v->ip, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_add_rules(const char *rule_path,
+		struct lpm_route_rule **proute_base,
+		int (*parser)(char *, struct lpm_route_rule *))
+{
+	struct lpm_route_rule *route_rules;
+	struct lpm_route_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
+static void
+free_lpm_routes(void)
+{
+	free(route_base_v4);
+	free(route_base_v6);
+	route_base_v4 = NULL;
+	route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
+}
+
 /* main processing loop */
 int
 lpm_main_loop(__rte_unused void *dummy)
@@ -153,6 +356,9 @@ lpm_main_loop(__rte_unused void *dummy)
 	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) /
 		US_PER_S * BURST_TX_DRAIN_US;
 
+	/* Config read and setup, free structs */
+	free_lpm_routes();
+
 	lcore_id = rte_lcore_id();
 	qconf = &lcore_conf[lcore_id];
 
@@ -548,13 +754,44 @@ lpm_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+/* Load rules from the input file */
+void
+read_config_files_lpm(void)
+{
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL) {
+		route_num_v4 = lpm_add_rules(parm_config.rule_ipv4_name,
+					&route_base_v4, &lpm_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			free_lpm_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "IPv4 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
+	}
+
+	/* ipv6 check */
+	if (parm_config.rule_ipv6_name != NULL) {
+		route_num_v6 = lpm_add_rules(parm_config.rule_ipv6_name,
+					&route_base_v6, &lpm_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			free_lpm_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "IPv6 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
+	}
+}
+
 void
 setup_lpm(const int socketid)
 {
 	struct rte_eth_dev_info dev_info;
 	struct rte_lpm6_config config;
 	struct rte_lpm_config config_ipv4;
-	unsigned i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -572,32 +809,33 @@ setup_lpm(const int socketid)
 			socketid);
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* skip unused ports */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			free_lpm_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
 		       inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
-		       ipv4_l3fwd_route_array[i].depth,
-		       ipv4_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v4[i].depth,
+		       route_base_v4[i].if_out, dev_info.device->name);
 	}
 
 	/* create the LPM6 table */
@@ -608,37 +846,40 @@ setup_lpm(const int socketid)
 	config.flags = 0;
 	ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid,
 				&config);
-	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL) {
+		free_lpm_routes();
 		rte_exit(EXIT_FAILURE,
 			"Unable to create the l3fwd LPM table on socket %d\n",
 			socketid);
+	}
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			free_lpm_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
-		       inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip, abuf,
+		       inet_ntop(AF_INET6, route_base_v6[i].ip_8, abuf,
 				 sizeof(abuf)),
-		       ipv6_l3fwd_route_array[i].depth,
-		       ipv6_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v6[i].depth,
+		       route_base_v6[i].if_out, dev_info.device->name);
 	}
 }
 
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index c7eba06c4d..6d41c926b1 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -14,6 +14,19 @@ struct ipv6_l3fwd_route {
 	uint8_t if_out;
 };
 
-extern const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[16];
+struct lpm_route_rule {
+	union {
+		uint32_t ip;
+		union {
+			uint32_t ip_32[4];
+			uint8_t ip_8[16];
+		};
+	};
+	uint8_t depth;
+	uint8_t if_out;
+};
 
-extern const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[16];
+extern struct lpm_route_rule *route_base_v4;
+extern struct lpm_route_rule *route_base_v6;
+extern int route_num_v4;
+extern int route_num_v6;
diff --git a/examples/l3fwd/lpm_default_v4.cfg b/examples/l3fwd/lpm_default_v4.cfg
new file mode 100644
index 0000000000..4db5597ed8
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD LPM
+R198.18.0.0/24 0
+R198.18.1.0/24 1
+R198.18.2.0/24 2
+R198.18.3.0/24 3
+R198.18.4.0/24 4
+R198.18.5.0/24 5
+R198.18.6.0/24 6
+R198.18.7.0/24 7
+R198.18.8.0/24 8
+R198.18.9.0/24 9
+R198.18.10.0/24 10
+R198.18.11.0/24 11
+R198.18.12.0/24 12
+R198.18.13.0/24 13
+R198.18.14.0/24 14
+R198.18.15.0/24 15
diff --git a/examples/l3fwd/lpm_default_v6.cfg b/examples/l3fwd/lpm_default_v6.cfg
new file mode 100644
index 0000000000..c50233e0ba
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD LPM
+R2001:0200:0000:0000:0000:0000:0000:0000/64 0
+R2001:0200:0000:0001:0000:0000:0000:0000/64 1
+R2001:0200:0000:0002:0000:0000:0000:0000/64 2
+R2001:0200:0000:0003:0000:0000:0000:0000/64 3
+R2001:0200:0000:0004:0000:0000:0000:0000/64 4
+R2001:0200:0000:0005:0000:0000:0000:0000/64 5
+R2001:0200:0000:0006:0000:0000:0000:0000/64 6
+R2001:0200:0000:0007:0000:0000:0000:0000/64 7
+R2001:0200:0000:0008:0000:0000:0000:0000/64 8
+R2001:0200:0000:0009:0000:0000:0000:0000/64 9
+R2001:0200:0000:000A:0000:0000:0000:0000/64 10
+R2001:0200:0000:000B:0000:0000:0000:0000/64 11
+R2001:0200:0000:000C:0000:0000:0000:0000/64 12
+R2001:0200:0000:000D:0000:0000:0000:0000/64 13
+R2001:0200:0000:000E:0000:0000:0000:0000/64 14
+R2001:0200:0000:000F:0000:0000:0000:0000/64 15
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index eb68ffc5aa..b02c14a5fc 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -94,6 +94,8 @@ uint32_t hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 
 struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+struct parm_cfg parm_config;
+
 struct lcore_params {
 	uint16_t port_id;
 	uint8_t queue_id;
@@ -141,6 +143,7 @@ static struct rte_mempool *vector_pool[RTE_MAX_ETHPORTS];
 static uint8_t lkp_per_socket[NB_SOCKETS];
 
 struct l3fwd_lkp_mode {
+	void  (*read_config_files)(void);
 	void  (*setup)(int);
 	int   (*check_ptype)(int);
 	rte_rx_callback_fn cb_parse_ptype;
@@ -152,6 +155,7 @@ struct l3fwd_lkp_mode {
 static struct l3fwd_lkp_mode l3fwd_lkp;
 
 static struct l3fwd_lkp_mode l3fwd_em_lkp = {
+	.read_config_files		= read_config_files_em,
 	.setup                  = setup_hash,
 	.check_ptype		= em_check_ptype,
 	.cb_parse_ptype		= em_cb_parse_ptype,
@@ -161,6 +165,7 @@ static struct l3fwd_lkp_mode l3fwd_em_lkp = {
 };
 
 static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_lpm,
 	.check_ptype		= lpm_check_ptype,
 	.cb_parse_ptype		= lpm_cb_parse_ptype,
@@ -170,6 +175,7 @@ static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
 };
 
 static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_fib,
 	.check_ptype            = lpm_check_ptype,
 	.cb_parse_ptype         = lpm_cb_parse_ptype,
@@ -179,50 +185,19 @@ static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
 };
 
 /*
- * 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
- * 198.18.{0-15}.0/24 = Port {0-15}
+ * API's called during initialization to setup ACL/EM/LPM rules.
  */
-const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[] = {
-	{RTE_IPV4(198, 18, 0, 0), 24, 0},
-	{RTE_IPV4(198, 18, 1, 0), 24, 1},
-	{RTE_IPV4(198, 18, 2, 0), 24, 2},
-	{RTE_IPV4(198, 18, 3, 0), 24, 3},
-	{RTE_IPV4(198, 18, 4, 0), 24, 4},
-	{RTE_IPV4(198, 18, 5, 0), 24, 5},
-	{RTE_IPV4(198, 18, 6, 0), 24, 6},
-	{RTE_IPV4(198, 18, 7, 0), 24, 7},
-	{RTE_IPV4(198, 18, 8, 0), 24, 8},
-	{RTE_IPV4(198, 18, 9, 0), 24, 9},
-	{RTE_IPV4(198, 18, 10, 0), 24, 10},
-	{RTE_IPV4(198, 18, 11, 0), 24, 11},
-	{RTE_IPV4(198, 18, 12, 0), 24, 12},
-	{RTE_IPV4(198, 18, 13, 0), 24, 13},
-	{RTE_IPV4(198, 18, 14, 0), 24, 14},
-	{RTE_IPV4(198, 18, 15, 0), 24, 15},
-};
+static void
+l3fwd_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
 
-/*
- * 2001:200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180).
- * 2001:200:0:{0-f}::/64 = Port {0-15}
- */
-const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[] = {
-	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 0},
-	{{32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 1},
-	{{32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 2},
-	{{32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 3},
-	{{32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 4},
-	{{32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 5},
-	{{32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 6},
-	{{32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 7},
-	{{32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 8},
-	{{32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 9},
-	{{32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 10},
-	{{32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 11},
-	{{32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 12},
-	{{32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 13},
-	{{32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 14},
-	{{32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 15},
-};
+static void
+l3fwd_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
 
 /*
  * Setup lookup methods for forwarding.
@@ -339,6 +314,8 @@ print_usage(const char *prgname)
 {
 	fprintf(stderr, "%s [EAL options] --"
 		" -p PORTMASK"
+		"  --rule_ipv4=FILE"
+		"  --rule_ipv6=FILE"
 		" [-P]"
 		" [--lookup]"
 		" --config (port,queue,lcore)[,(port,queue,lcore)]"
@@ -382,6 +359,9 @@ print_usage(const char *prgname)
 		"  --event-vector-tmo: Max timeout to form vector in nanoseconds if event vectorization is enabled\n"
 		"  -E : Enable exact match, legacy flag please use --lookup=em instead\n"
 		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n\n",
+		"  --rule_ipv4=FILE: specify the ipv4 rules entries file. "
+		"Each rule occupies one line. "
+		"  --rule_ipv6=FILE: specify the ipv6 rules entries file.\n",
 		prgname);
 }
 
@@ -596,6 +576,8 @@ static const char short_options[] =
 #define CMD_LINE_OPT_ENABLE_VECTOR "event-vector"
 #define CMD_LINE_OPT_VECTOR_SIZE "event-vector-size"
 #define CMD_LINE_OPT_VECTOR_TMO_NS "event-vector-tmo"
+#define CMD_LINE_OPT_RULE_IPV4 "rule_ipv4"
+#define CMD_LINE_OPT_RULE_IPV6 "rule_ipv6"
 
 enum {
 	/* long options mapped to a short option */
@@ -610,6 +592,8 @@ enum {
 	CMD_LINE_OPT_MAX_PKT_LEN_NUM,
 	CMD_LINE_OPT_HASH_ENTRY_NUM_NUM,
 	CMD_LINE_OPT_PARSE_PTYPE_NUM,
+	CMD_LINE_OPT_RULE_IPV4_NUM,
+	CMD_LINE_OPT_RULE_IPV6_NUM,
 	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
 	CMD_LINE_OPT_MODE_NUM,
 	CMD_LINE_OPT_EVENTQ_SYNC_NUM,
@@ -637,6 +621,8 @@ static const struct option lgopts[] = {
 	{CMD_LINE_OPT_ENABLE_VECTOR, 0, 0, CMD_LINE_OPT_ENABLE_VECTOR_NUM},
 	{CMD_LINE_OPT_VECTOR_SIZE, 1, 0, CMD_LINE_OPT_VECTOR_SIZE_NUM},
 	{CMD_LINE_OPT_VECTOR_TMO_NS, 1, 0, CMD_LINE_OPT_VECTOR_TMO_NS_NUM},
+	{CMD_LINE_OPT_RULE_IPV4,   1, 0, CMD_LINE_OPT_RULE_IPV4_NUM},
+	{CMD_LINE_OPT_RULE_IPV6,   1, 0, CMD_LINE_OPT_RULE_IPV6_NUM},
 	{NULL, 0, 0, 0}
 };
 
@@ -791,6 +777,12 @@ parse_args(int argc, char **argv)
 		case CMD_LINE_OPT_VECTOR_TMO_NS_NUM:
 			evt_rsrc->vector_tmo_ns = strtoull(optarg, NULL, 10);
 			break;
+		case CMD_LINE_OPT_RULE_IPV4_NUM:
+			l3fwd_set_rule_ipv4_name(optarg);
+			break;
+		case CMD_LINE_OPT_RULE_IPV6_NUM:
+			l3fwd_set_rule_ipv6_name(optarg);
+			break;
 		default:
 			print_usage(prgname);
 			return -1;
@@ -1358,6 +1350,24 @@ l3fwd_event_service_setup(void)
 	}
 }
 
+/* Bypass comment and empty lines */
+int
+is_bypass_line(const char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
 int
 main(int argc, char **argv)
 {
@@ -1395,6 +1405,9 @@ main(int argc, char **argv)
 	/* Setup function pointers for lookup method. */
 	setup_l3fwd_lookup_tables();
 
+	/* Add the config file rules */
+	l3fwd_lkp.read_config_files();
+
 	evt_rsrc->per_port_pool = per_port_pool;
 	evt_rsrc->pkt_pool = pktmbuf_pool;
 	evt_rsrc->vec_pool = vector_pool;
-- 
2.25.1


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

* [PATCH v1 2/2] examples/l3fwd: add config file support for EM
  2021-12-16 15:01 [PATCH v1 0/2] Add config file support for l3fwd Sean Morrissey
  2021-12-16 15:01 ` [PATCH v1 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
@ 2021-12-16 15:01 ` Sean Morrissey
  2021-12-20 11:08 ` [PATCH v2 0/2] Add config file support for l3fwd Sean Morrissey
  2 siblings, 0 replies; 46+ messages in thread
From: Sean Morrissey @ 2021-12-16 15:01 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Ravi Kerur

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for EM with a format
similar to l3fwd-acl one.

With the removal of the hardcoded route tables for IPv4
and IPv6 from 'l3fwd_em', these routes have been moved
to a separate default config file for use with EM.

Related l3fwd docs have been updated to relfect these
changes.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
 examples/l3fwd/em_default_v4.cfg        |  17 +
 examples/l3fwd/em_default_v6.cfg        |  17 +
 examples/l3fwd/l3fwd_em.c               | 473 ++++++++++++++----------
 examples/l3fwd/l3fwd_route.h            |  38 +-
 5 files changed, 412 insertions(+), 222 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg

diff --git a/doc/guides/sample_app_ug/l3_forward.rst b/doc/guides/sample_app_ug/l3_forward.rst
index 6d7d7c5cc1..01d86db95d 100644
--- a/doc/guides/sample_app_ug/l3_forward.rst
+++ b/doc/guides/sample_app_ug/l3_forward.rst
@@ -47,6 +47,7 @@ and loaded into the LPM or FIB object at initialization time.
 In the sample application, hash-based and FIB-based forwarding supports
 both IPv4 and IPv6.
 LPM-based forwarding supports IPv4 only.
+During the initialization phase route rules for IPv4 and IPv6 are read from rule files.
 
 Compiling the Application
 -------------------------
@@ -61,6 +62,8 @@ Running the Application
 The application has a number of command line options::
 
     ./dpdk-l3fwd [EAL options] -- -p PORTMASK
+                             --rule_ipv4=FILE
+                             --rule_ipv6=FILE
                              [-P]
                              [--lookup LOOKUP_METHOD]
                              --config(port,queue,lcore)[,(port,queue,lcore)]
@@ -82,6 +85,11 @@ Where,
 
 * ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
 
+* ``--rule_ipv4=FILE:`` specify the ipv4 rules entries file.
+  Each rule occupies one line.
+
+* ``--rule_ipv6=FILE:`` specify the ipv6 rules entries file.
+
 * ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
   Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
 
@@ -135,7 +143,7 @@ To enable L3 forwarding between two ports, assuming that both ports are in the s
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
+    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)" --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -157,19 +165,23 @@ In this command:
 |          |           |           |                                     |
 +----------+-----------+-----------+-------------------------------------+
 
+*   The -rule_ipv4 option specifies the reading of IPv4 rules sets from the rule_ipv4.cfg file
+
+*   The -rule_ipv6 option specifies the reading of IPv6 rules sets from the rule_ipv6.cfg file.
+
 To use eventdev mode with sync method **ordered** on above mentioned environment,
 Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 or
 
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> \
-		-- -p 0x03 --mode=eventdev --eventq-sched=ordered
+		-- -p 0x03 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -192,7 +204,7 @@ scheduler. Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In case of eventdev mode, *--config* option is not used for ethernet port
 configuration. Instead each ethernet port will be configured with mentioned
@@ -216,6 +228,49 @@ The following sections provide some explanation of the sample application code.
 the initialization and run-time paths are very similar to those of the :doc:`l2_forward_real_virtual` and :doc:`l2_forward_event`.
 The following sections describe aspects that are specific to the L3 Forwarding sample application.
 
+Parse Rules from File
+~~~~~~~~~~~~~~~~~~~~~
+
+The application parses the rules from the file and adds them to the appropriate route table by calling the appropriate function.
+It ignores empty and comment lines, and parses and validates the rules it reads.
+If errors are detected, the application exits with messages to identify the errors encountered.
+
+The format of the route rules differs based on which lookup method is being used.
+Therefore, the code only decreases the priority number with each rule it parses.
+Route rules are mandatory.
+To read data from the specified file successfully, the application assumes the following:
+
+*   Each rule occupies a single line.
+
+*   Only the following four rule line types are valid in this application:
+
+*   Route rule line, which starts with a leading character 'R'
+
+*   Comment line, which starts with a leading character '#'
+
+*   Empty line, which consists of a space, form-feed ('\f'), newline ('\n'),
+    carriage return ('\r'), horizontal tab ('\t'), or vertical tab ('\v').
+
+Other lines types are considered invalid.
+
+*   Rules are organized in descending order of priority,
+    which means rules at the head of the file always have a higher priority than those further down in the file.
+
+*   A typical IPv4 LPM/FIB rule line should have a format as shown below:
+
+R<destination_ip>/<ip_mask_length><output_port_number>
+
+*   A typical IPv4 EM rule line should have a format as shown below:
+
+R<destination_ip><source_ip><destination_port><source_port><protocol><output_port_number>
+
+IPv4 addresses are specified in CIDR format as specified in RFC 4632.
+For LPM/FIB they consist of the dot notation for the address and a prefix length separated by '/'.
+For example, 192.168.0.34/32, where the address is 192.168.0.34 and the prefix length is 32.
+For EM they consist of just the dot notation for the address and no prefix length.
+For example, 192.168.0.34, where the Address is 192.168.0.34.
+EM also includes ports which are specified as a single number which represents a single port.
+
 Hash Initialization
 ~~~~~~~~~~~~~~~~~~~
 
@@ -227,8 +282,6 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
 
     The Hash initialization will setup both ipv4 and ipv6 hash table,
     and populate the either table depending on the value of variable ipv6.
-    To support the hash performance test with up to 8M single direction flows/16M bi-direction flows,
-    populate_ipv4_many_flow_into_table() function will populate the hash table with specified hash table entry number(default 4M).
 
 .. note::
 
@@ -246,22 +299,14 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
         {
             // ...
 
-            if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_many_flow_into_table(ipv4_l3fwd_lookup_struct[socketid], hash_entry_number);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_many_flow_into_table( ipv6_l3fwd_lookup_struct[socketid], hash_entry_number);
-                }
-            } else
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_few_flow_into_table(ipv4_l3fwd_lookup_struct[socketid]);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_few_flow_into_table(ipv6_l3fwd_lookup_struct[socketid]);
-                }
+            if (ipv6 == 0) {
+                /* populate the ipv4 hash */
+                populate_ipv4_flow_into_table(
+                    ipv4_l3fwd_em_lookup_struct[socketid]);
+            } else {
+                /* populate the ipv6 hash */
+                populate_ipv6_flow_into_table(
+                    ipv6_l3fwd_em_lookup_struct[socketid]);
             }
         }
     #endif
diff --git a/examples/l3fwd/em_default_v4.cfg b/examples/l3fwd/em_default_v4.cfg
new file mode 100644
index 0000000000..b70bdb6601
--- /dev/null
+++ b/examples/l3fwd/em_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD EM
+R198.18.0.0 198.18.0.1 9 9 0x11 0
+R198.18.1.0 198.18.1.1 9 9 0x11 1
+R198.18.2.0 198.18.2.1 9 9 0x11 2
+R198.18.3.0 198.18.3.1 9 9 0x11 3
+R198.18.4.0 198.18.4.1 9 9 0x11 4
+R198.18.5.0 198.18.5.1 9 9 0x11 5
+R198.18.6.0 198.18.6.1 9 9 0x11 6
+R198.18.7.0 198.18.7.1 9 9 0x11 7
+R198.18.8.0 198.18.8.1 9 9 0x11 8
+R198.18.9.0 198.18.9.1 9 9 0x11 9
+R198.18.10.0 198.18.10.1 9 9 0x11 10
+R198.18.11.0 198.18.11.1 9 9 0x11 11
+R198.18.12.0 198.18.12.1 9 9 0x11 12
+R198.18.13.0 198.18.13.1 9 9 0x11 13
+R198.18.14.0 198.18.14.1 9 9 0x11 14
+R198.18.15.0 198.18.15.1 9 9 0x11 15
diff --git a/examples/l3fwd/em_default_v6.cfg b/examples/l3fwd/em_default_v6.cfg
new file mode 100644
index 0000000000..a18519ff2d
--- /dev/null
+++ b/examples/l3fwd/em_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD EM
+R2001:0200:0000:0000:0000:0000:0000:0000 2001:0200:0000:0000:0000:0000:0000:0001 9 9 0x11 0
+R2001:0200:0000:0001:0000:0000:0000:0000 2001:0200:0000:0001:0000:0000:0000:0001 9 9 0x11 1
+R2001:0200:0000:0002:0000:0000:0000:0000 2001:0200:0000:0002:0000:0000:0000:0001 9 9 0x11 2
+R2001:0200:0000:0003:0000:0000:0000:0000 2001:0200:0000:0003:0000:0000:0000:0001 9 9 0x11 3
+R2001:0200:0000:0004:0000:0000:0000:0000 2001:0200:0000:0004:0000:0000:0000:0001 9 9 0x11 4
+R2001:0200:0000:0005:0000:0000:0000:0000 2001:0200:0000:0005:0000:0000:0000:0001 9 9 0x11 5
+R2001:0200:0000:0006:0000:0000:0000:0000 2001:0200:0000:0006:0000:0000:0000:0001 9 9 0x11 6
+R2001:0200:0000:0007:0000:0000:0000:0000 2001:0200:0000:0007:0000:0000:0000:0001 9 9 0x11 7
+R2001:0200:0000:0008:0000:0000:0000:0000 2001:0200:0000:0008:0000:0000:0000:0001 9 9 0x11 8
+R2001:0200:0000:0009:0000:0000:0000:0000 2001:0200:0000:0009:0000:0000:0000:0001 9 9 0x11 9
+R2001:0200:0000:000A:0000:0000:0000:0000 2001:0200:0000:000A:0000:0000:0000:0001 9 9 0x11 10
+R2001:0200:0000:000B:0000:0000:0000:0000 2001:0200:0000:000B:0000:0000:0000:0001 9 9 0x11 11
+R2001:0200:0000:000C:0000:0000:0000:0000 2001:0200:0000:000C:0000:0000:0000:0001 9 9 0x11 12
+R2001:0200:0000:000D:0000:0000:0000:0000 2001:0200:0000:000D:0000:0000:0000:0001 9 9 0x11 13
+R2001:0200:0000:000E:0000:0000:0000:0000 2001:0200:0000:000E:0000:0000:0000:0001 9 9 0x11 14
+R2001:0200:0000:000F:0000:0000:0000:0000 2001:0200:0000:000F:0000:0000:0000:0001 9 9 0x11 15
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 4953cdae4e..9a337ed842 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -27,6 +27,7 @@
 
 #include "l3fwd.h"
 #include "l3fwd_event.h"
+#include "l3fwd_route.h"
 
 #if defined(RTE_ARCH_X86) || defined(__ARM_FEATURE_CRC32)
 #define EM_HASH_CRC 1
@@ -42,13 +43,18 @@
 
 #define IPV6_ADDR_LEN 16
 
-struct ipv4_5tuple {
-	uint32_t ip_dst;
-	uint32_t ip_src;
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
+static struct em_rule *em_route_base_v4;
+static struct em_rule *em_route_base_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_SRC_ADDR,
+	CB_FLD_DST_PORT,
+	CB_FLD_SRC_PORT,
+	CB_FLD_PROTO,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
 
 union ipv4_5tuple_host {
 	struct {
@@ -65,14 +71,6 @@ union ipv4_5tuple_host {
 
 #define XMM_NUM_IN_IPV6_5TUPLE 3
 
-struct ipv6_5tuple {
-	uint8_t  ip_dst[IPV6_ADDR_LEN];
-	uint8_t  ip_src[IPV6_ADDR_LEN];
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
-
 union ipv6_5tuple_host {
 	struct {
 		uint16_t pad0;
@@ -87,8 +85,6 @@ union ipv6_5tuple_host {
 	xmm_t xmm[XMM_NUM_IN_IPV6_5TUPLE];
 };
 
-
-
 struct ipv4_l3fwd_em_route {
 	struct ipv4_5tuple key;
 	uint8_t if_out;
@@ -99,66 +95,6 @@ struct ipv6_l3fwd_em_route {
 	uint8_t if_out;
 };
 
-/* 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
- * Use RFC863 Discard Protocol.
- */
-static const struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[] = {
-	{{RTE_IPV4(198, 18, 0, 0), RTE_IPV4(198, 18, 0, 1),  9, 9, IPPROTO_UDP}, 0},
-	{{RTE_IPV4(198, 18, 1, 0), RTE_IPV4(198, 18, 1, 1),  9, 9, IPPROTO_UDP}, 1},
-	{{RTE_IPV4(198, 18, 2, 0), RTE_IPV4(198, 18, 2, 1),  9, 9, IPPROTO_UDP}, 2},
-	{{RTE_IPV4(198, 18, 3, 0), RTE_IPV4(198, 18, 3, 1),  9, 9, IPPROTO_UDP}, 3},
-	{{RTE_IPV4(198, 18, 4, 0), RTE_IPV4(198, 18, 4, 1),  9, 9, IPPROTO_UDP}, 4},
-	{{RTE_IPV4(198, 18, 5, 0), RTE_IPV4(198, 18, 5, 1),  9, 9, IPPROTO_UDP}, 5},
-	{{RTE_IPV4(198, 18, 6, 0), RTE_IPV4(198, 18, 6, 1),  9, 9, IPPROTO_UDP}, 6},
-	{{RTE_IPV4(198, 18, 7, 0), RTE_IPV4(198, 18, 7, 1),  9, 9, IPPROTO_UDP}, 7},
-	{{RTE_IPV4(198, 18, 8, 0), RTE_IPV4(198, 18, 8, 1),  9, 9, IPPROTO_UDP}, 8},
-	{{RTE_IPV4(198, 18, 9, 0), RTE_IPV4(198, 18, 9, 1),  9, 9, IPPROTO_UDP}, 9},
-	{{RTE_IPV4(198, 18, 10, 0), RTE_IPV4(198, 18, 10, 1),  9, 9, IPPROTO_UDP}, 10},
-	{{RTE_IPV4(198, 18, 11, 0), RTE_IPV4(198, 18, 11, 1),  9, 9, IPPROTO_UDP}, 11},
-	{{RTE_IPV4(198, 18, 12, 0), RTE_IPV4(198, 18, 12, 1),  9, 9, IPPROTO_UDP}, 12},
-	{{RTE_IPV4(198, 18, 13, 0), RTE_IPV4(198, 18, 13, 1),  9, 9, IPPROTO_UDP}, 13},
-	{{RTE_IPV4(198, 18, 14, 0), RTE_IPV4(198, 18, 14, 1),  9, 9, IPPROTO_UDP}, 14},
-	{{RTE_IPV4(198, 18, 15, 0), RTE_IPV4(198, 18, 15, 1),  9, 9, IPPROTO_UDP}, 15},
-};
-
-/* 2001:0200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180).
- * Use RFC863 Discard Protocol.
- */
-static const struct ipv6_l3fwd_em_route ipv6_l3fwd_em_route_array[] = {
-	{{{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 0},
-	{{{32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 1},
-	{{{32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 2},
-	{{{32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 3},
-	{{{32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 4},
-	{{{32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 5},
-	{{{32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 6},
-	{{{32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 7},
-	{{{32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 8},
-	{{{32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 9},
-	{{{32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 10},
-	{{{32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 11},
-	{{{32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 12},
-	{{{32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 13},
-	{{{32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 14},
-	{{{32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 15},
-};
-
 struct rte_hash *ipv4_l3fwd_em_lookup_struct[NB_SOCKETS];
 struct rte_hash *ipv6_l3fwd_em_lookup_struct[NB_SOCKETS];
 
@@ -235,10 +171,6 @@ ipv6_hash_crc(const void *data, __rte_unused uint32_t data_len,
 	return init_val;
 }
 
-#define IPV4_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv4_l3fwd_em_route_array)
-
-#define IPV6_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv6_l3fwd_em_route_array)
-
 static uint8_t ipv4_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 static uint8_t ipv6_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 
@@ -346,6 +278,178 @@ em_get_ipv6_dst_port(void *ipv6_hdr, uint16_t portid, void *lookup_struct)
 #include "l3fwd_em.h"
 #endif
 
+static int
+em_parse_v6_net(const char *in, uint32_t *v)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = inet_pton(AF_INET6, in, &v);
+	if (rc != 1)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+em_parse_v6_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	uint32_t ip[4];
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	ip[0] = *(v->v6_key.ip32_dst);
+	rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], ip);
+	if (rc != 0)
+		return rc;
+	ip[0] = *(v->v6_key.ip32_src);
+	rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], ip);
+	if (rc != 0)
+		return rc;
+
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_parse_v4_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = inet_pton(AF_INET, in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
+	v->v4_key.ip_dst = ntohl(v->v4_key.ip_dst);
+	if (rc != 1)
+		return rc;
+
+	rc = inet_pton(AF_INET, in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
+	v->v4_key.ip_src = ntohl(v->v4_key.ip_src);
+	if (rc != 1)
+		return rc;
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_add_rules(const char *rule_path,
+		struct em_rule **proute_base,
+		int (*parser)(char *, struct em_rule *))
+{
+	struct em_rule *route_rules;
+	struct em_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
 static void
 convert_ipv4_5tuple(struct ipv4_5tuple *key1,
 		union ipv4_5tuple_host *key2)
@@ -382,122 +486,92 @@ convert_ipv6_5tuple(struct ipv6_5tuple *key1,
 #define BIT_8_TO_15 0x0000ff00
 
 static inline void
-populate_ipv4_few_flow_into_table(const struct rte_hash *h)
+populate_ipv4_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
-	for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv4_l3fwd_em_route  entry;
+	for (i = 0; i < route_num_v4; i++) {
+		struct em_rule *entry;
 		union ipv4_5tuple_host newkey;
+		struct in_addr src;
+		struct in_addr dst;
 
-		entry = ipv4_l3fwd_em_route_array[i];
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v4[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v4[i];
+		convert_ipv4_5tuple(&(entry->v4_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv4_l3fwd_out_if[ret] = entry.if_out;
+		ipv4_l3fwd_out_if[ret] = entry->if_out;
+		rte_eth_dev_info_get(em_route_base_v4[i].if_out,
+				     &dev_info);
+
+		src.s_addr = htonl(em_route_base_v4[i].v4_key.ip_src);
+		dst.s_addr = htonl(em_route_base_v4[i].v4_key.ip_dst);
+		printf("EM: Adding route %s %s (%d) [%s]\n",
+			   inet_ntop(AF_INET, &dst, dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET, &src, srcbuf, sizeof(srcbuf)),
+		       em_route_base_v4[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 " keys\n",
-		(uint64_t)IPV4_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v4);
 }
 
 #define BIT_16_TO_23 0x00ff0000
 static inline void
-populate_ipv6_few_flow_into_table(const struct rte_hash *h)
+populate_ipv6_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
-	for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv6_l3fwd_em_route entry;
+	for (i = 0; i < route_num_v6; i++) {
+		struct em_rule *entry;
 		union ipv6_5tuple_host newkey;
 
-		entry = ipv6_l3fwd_em_route_array[i];
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v6[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v6[i];
+		convert_ipv6_5tuple(&(entry->v6_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv6_l3fwd_out_if[ret] = entry.if_out;
+		ipv6_l3fwd_out_if[ret] = entry->if_out;
+		rte_eth_dev_info_get(em_route_base_v6[i].if_out,
+				     &dev_info);
+
+		printf("EM: Adding route %s %s (%d) [%s]\n",
+			   inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip32_dst,
+			   dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip32_src,
+			   srcbuf, sizeof(srcbuf)),
+		       em_route_base_v6[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 "keys\n",
-		(uint64_t)IPV6_L3FWD_EM_NUM_ROUTES);
-}
-
-#define NUMBER_PORT_USED 16
-static inline void
-populate_ipv4_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv4_l3fwd_em_route entry;
-		union ipv4_5tuple_host newkey;
-
-		uint8_t a = (uint8_t)((port + 1) % BYTE_VALUE_MAX);
-
-		/* Create the ipv4 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv4_l3fwd_em_route_array[port];
-		entry.key.ip_dst = RTE_IPV4(198, 18, port, a);
-		convert_ipv4_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv4_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
-}
-
-static inline void
-populate_ipv6_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv6_l3fwd_em_route entry;
-		union ipv6_5tuple_host newkey;
-
-		/* Create the ipv6 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv6_l3fwd_em_route_array[port];
-		entry.key.ip_dst[15] = (port + 1) % BYTE_VALUE_MAX;
-		convert_ipv6_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv6_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
+		(uint64_t)route_num_v6);
 }
 
 /* Requirements:
@@ -972,17 +1046,53 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+static void
+free_em_routes(void)
+{
+	free(em_route_base_v4);
+	free(em_route_base_v6);
+	em_route_base_v4 = NULL;
+	em_route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
+}
+
 /* Load rules from the input file */
 void
 read_config_files_em(void)
 {
-	/* Empty till config file support added to EM */
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL) {
+		route_num_v4 = em_add_rules(parm_config.rule_ipv4_name,
+					&em_route_base_v4, &em_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			free_em_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv4 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "EM IPv4 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+	}
+
+	/* ipv6 check */
+	if (parm_config.rule_ipv6_name != NULL) {
+		route_num_v6 = em_add_rules(parm_config.rule_ipv6_name,
+					&em_route_base_v6, &em_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			free_em_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "EM IPv6 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+	}
 }
 
 /* Initialize exact match (hash) parameters. 8< */
 void
 setup_hash(const int socketid)
 {
+	printf("IPPPROTO_UDP: %d\n", IPPROTO_UDP);
 	struct rte_hash_parameters ipv4_l3fwd_hash_params = {
 		.name = NULL,
 		.entries = L3FWD_HASH_ENTRIES,
@@ -1023,35 +1133,18 @@ setup_hash(const int socketid)
 			"Unable to create the l3fwd hash on socket %d\n",
 			socketid);
 
-	if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-		/* For testing hash matching with a large number of flows we
-		 * generate millions of IP 5-tuples with an incremented dst
-		 * address to initialize the hash table. */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_many_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_many_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		}
+	/*
+	 * Use data from ipv4/ipv6 l3fwd config file
+	 * directly to initialize the hash table.
+	 */
+	if (ipv6 == 0) {
+		/* populate the ipv4 hash */
+		populate_ipv4_flow_into_table(
+			ipv4_l3fwd_em_lookup_struct[socketid]);
 	} else {
-		/*
-		 * Use data in ipv4/ipv6 l3fwd lookup table
-		 * directly to initialize the hash table.
-		 */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_few_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid]);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_few_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid]);
-		}
+		/* populate the ipv6 hash */
+		populate_ipv6_flow_into_table(
+			ipv6_l3fwd_em_lookup_struct[socketid]);
 	}
 }
 /* >8 End of initialization of hash parameters. */
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index 6d41c926b1..a3ac991407 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -2,17 +2,27 @@
  * Copyright(c) 2021 Intel Corporation
  */
 
-struct ipv4_l3fwd_route {
-	uint32_t ip;
-	uint8_t  depth;
-	uint8_t  if_out;
-};
+struct ipv4_5tuple {
+	uint32_t ip_dst;
+	uint32_t ip_src;
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
 
-struct ipv6_l3fwd_route {
-	uint8_t ip[16];
-	uint8_t depth;
-	uint8_t if_out;
-};
+struct ipv6_5tuple {
+	union {
+		uint8_t  ip_dst[IPV6_ADDR_LEN];
+		uint32_t ip32_dst[4];
+	};
+	union {
+		uint8_t  ip_src[IPV6_ADDR_LEN];
+		uint32_t ip32_src[4];
+	};
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
 
 struct lpm_route_rule {
 	union {
@@ -26,6 +36,14 @@ struct lpm_route_rule {
 	uint8_t if_out;
 };
 
+struct em_rule {
+		union {
+		struct ipv4_5tuple v4_key;
+		struct ipv6_5tuple v6_key;
+	};
+	uint8_t if_out;
+};
+
 extern struct lpm_route_rule *route_base_v4;
 extern struct lpm_route_rule *route_base_v6;
 extern int route_num_v4;
-- 
2.25.1


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

* [PATCH v2 0/2] Add config file support for l3fwd
  2021-12-16 15:01 [PATCH v1 0/2] Add config file support for l3fwd Sean Morrissey
  2021-12-16 15:01 ` [PATCH v1 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
  2021-12-16 15:01 ` [PATCH v1 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
@ 2021-12-20 11:08 ` Sean Morrissey
  2021-12-20 11:08   ` [PATCH v2 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
                     ` (2 more replies)
  2 siblings, 3 replies; 46+ messages in thread
From: Sean Morrissey @ 2021-12-20 11:08 UTC (permalink / raw)
  Cc: dev, Sean Morrissey

This patchset introduces config file support for l3fwd
and its lookup methods LPM, FIB, and EM, similar to
that of l3fwd-acl. This allows for route rules to be
defined in configuration files and edited there instead
of in each of the lookup methods hardcoded route tables.

Sean Morrissey (2):
  examples/l3fwd: add config file support for LPM/FIB
  examples/l3fwd: add config file support for EM

 doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
 examples/l3fwd/em_default_v4.cfg        |  17 +
 examples/l3fwd/em_default_v6.cfg        |  17 +
 examples/l3fwd/l3fwd.h                  |  35 ++
 examples/l3fwd/l3fwd_em.c               | 478 ++++++++++++++----------
 examples/l3fwd/l3fwd_fib.c              |  52 +--
 examples/l3fwd/l3fwd_lpm.c              | 281 +++++++++++++-
 examples/l3fwd/l3fwd_route.h            |  49 ++-
 examples/l3fwd/lpm_default_v4.cfg       |  17 +
 examples/l3fwd/lpm_default_v6.cfg       |  17 +
 examples/l3fwd/main.c                   |  99 ++---
 11 files changed, 846 insertions(+), 305 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg

-- 
2.25.1


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

* [PATCH v2 1/2] examples/l3fwd: add config file support for LPM/FIB
  2021-12-20 11:08 ` [PATCH v2 0/2] Add config file support for l3fwd Sean Morrissey
@ 2021-12-20 11:08   ` Sean Morrissey
  2021-12-20 15:42     ` Ananyev, Konstantin
  2021-12-20 11:08   ` [PATCH v2 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
  2021-12-21 12:30   ` [PATCH v3 0/2] Add config file support for l3fwd Sean Morrissey
  2 siblings, 1 reply; 46+ messages in thread
From: Sean Morrissey @ 2021-12-20 11:08 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Ravi Kerur

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for LPM and FIB,
with format similar to l3fwd-acl one.

With the removal of the hardcoded route tables for IPv4
and IPv6, these routes have been moved to a separate
default config file for use with LPM and FIB.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 examples/l3fwd/l3fwd.h            |  35 ++++
 examples/l3fwd/l3fwd_em.c         |   7 +
 examples/l3fwd/l3fwd_fib.c        |  52 +++---
 examples/l3fwd/l3fwd_lpm.c        | 281 +++++++++++++++++++++++++++---
 examples/l3fwd/l3fwd_route.h      |  17 +-
 examples/l3fwd/lpm_default_v4.cfg |  17 ++
 examples/l3fwd/lpm_default_v6.cfg |  17 ++
 examples/l3fwd/main.c             |  99 ++++++-----
 8 files changed, 438 insertions(+), 87 deletions(-)
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg

diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 38ca19133c..d8b1f971e1 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -58,6 +58,30 @@
 #endif
 #define HASH_ENTRY_NUMBER_DEFAULT	16
 
+/*Log file related character defs. */
+#define COMMENT_LEAD_CHAR	('#')
+#define ROUTE_LEAD_CHAR		('R')
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+struct parm_cfg {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+};
+
 struct mbuf_table {
 	uint16_t len;
 	struct rte_mbuf *m_table[MAX_PKT_BURST];
@@ -96,6 +120,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern struct parm_cfg parm_config;
+
 /* Send burst of packets on an output interface */
 static inline int
 send_burst(struct lcore_conf *qconf, uint16_t n, uint16_t port)
@@ -183,6 +209,12 @@ int
 init_mem(uint16_t portid, unsigned int nb_mbuf);
 
 /* Function pointers for LPM, EM or FIB functionality. */
+void
+read_config_files_lpm(void);
+
+void
+read_config_files_em(void);
+
 void
 setup_lpm(const int socketid);
 
@@ -286,4 +318,7 @@ fib_get_ipv4_l3fwd_lookup_struct(const int socketid);
 void *
 fib_get_ipv6_l3fwd_lookup_struct(const int socketid);
 
+int
+is_bypass_line(const char *buff);
+
 #endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 5cc4a4d979..4953cdae4e 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -972,6 +972,13 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+/* Load rules from the input file */
+void
+read_config_files_em(void)
+{
+	/* Empty till config file support added to EM */
+}
+
 /* Initialize exact match (hash) parameters. 8< */
 void
 setup_hash(const int socketid)
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 2110459cc3..003721c908 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -583,7 +583,7 @@ setup_fib(const int socketid)
 	struct rte_eth_dev_info dev_info;
 	struct rte_fib6_conf config;
 	struct rte_fib_conf config_ipv4;
-	unsigned int i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -603,37 +603,39 @@ setup_fib(const int socketid)
 			"Unable to create the l3fwd FIB table on socket %d\n",
 			socketid);
 
+
 	/* Populate the fib ipv4 table. */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* Skip unused ports. */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_fib_add(ipv4_l3fwd_fib_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		if (inet_ntop(AF_INET, &in, abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv4_l3fwd_route_array[i].depth,
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].depth,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv4 route added to port %d [%s]\n",
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		}
 	}
@@ -650,44 +652,50 @@ setup_fib(const int socketid)
 	config.trie.num_tbl8 = (1 << 15);
 	ipv6_l3fwd_fib_lookup_struct[socketid] = rte_fib6_create(s, socketid,
 			&config);
-	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL) {
+		free(route_base_v4);
 		rte_exit(EXIT_FAILURE,
 				"Unable to create the l3fwd FIB table on socket %d\n",
 				socketid);
+	}
 
 	/* Populate the fib IPv6 table. */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* Skip unused ports. */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv6_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_fib6_add(ipv6_l3fwd_fib_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
+			free(route_base_v6);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		if (inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip,
+		if (inet_ntop(AF_INET6, route_base_v6[i].ip_8,
 				abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv6_l3fwd_route_array[i].depth,
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].depth,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv6 route added to port %d [%s]\n",
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		}
 	}
+	free(route_base_v4);
+	free(route_base_v6);
 }
 
 /* Return ipv4 fib lookup struct. */
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index a5b476ced3..4157f16c07 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -39,6 +39,16 @@
 
 static struct rte_lpm *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 static struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
+struct lpm_route_rule *route_base_v4;
+struct lpm_route_rule *route_base_v6;
+int route_num_v4;
+int route_num_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
 
 /* Performing LPM-based lookups. 8< */
 static inline uint16_t
@@ -139,6 +149,199 @@ lpm_get_dst_port_with_ipv4(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
 #include "l3fwd_lpm.h"
 #endif
 
+static int
+parse_ipv6_addr_mask(char *token, uint32_t *ipv6, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET6, sa, ipv6) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 128, 0);
+	return 0;
+}
+
+static int
+parse_ipv4_addr_mask(char *token, uint32_t *ipv4, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET, sa, ipv4) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 32, 0);
+	*ipv4 = ntohl(*ipv4);
+	return 0;
+}
+
+static int
+lpm_parse_v6_net(char *in, uint32_t *v, uint8_t *mask_len)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = parse_ipv6_addr_mask(in, v, mask_len);
+
+	return rc;
+}
+
+static int
+lpm_parse_v6_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], v->ip_32, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_parse_v4_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = parse_ipv4_addr_mask(in[CB_FLD_DST_ADDR], &v->ip, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_add_rules(const char *rule_path,
+		struct lpm_route_rule **proute_base,
+		int (*parser)(char *, struct lpm_route_rule *))
+{
+	struct lpm_route_rule *route_rules;
+	struct lpm_route_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
+static void
+free_lpm_routes(void)
+{
+	free(route_base_v4);
+	free(route_base_v6);
+	route_base_v4 = NULL;
+	route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
+}
+
 /* main processing loop */
 int
 lpm_main_loop(__rte_unused void *dummy)
@@ -153,6 +356,9 @@ lpm_main_loop(__rte_unused void *dummy)
 	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) /
 		US_PER_S * BURST_TX_DRAIN_US;
 
+	/* Config read and setup, free structs */
+	free_lpm_routes();
+
 	lcore_id = rte_lcore_id();
 	qconf = &lcore_conf[lcore_id];
 
@@ -548,13 +754,44 @@ lpm_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+/* Load rules from the input file */
+void
+read_config_files_lpm(void)
+{
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL) {
+		route_num_v4 = lpm_add_rules(parm_config.rule_ipv4_name,
+					&route_base_v4, &lpm_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			free_lpm_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "IPv4 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
+	}
+
+	/* ipv6 check */
+	if (parm_config.rule_ipv6_name != NULL) {
+		route_num_v6 = lpm_add_rules(parm_config.rule_ipv6_name,
+					&route_base_v6, &lpm_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			free_lpm_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "IPv6 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
+	}
+}
+
 void
 setup_lpm(const int socketid)
 {
 	struct rte_eth_dev_info dev_info;
 	struct rte_lpm6_config config;
 	struct rte_lpm_config config_ipv4;
-	unsigned i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -572,32 +809,33 @@ setup_lpm(const int socketid)
 			socketid);
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* skip unused ports */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			free_lpm_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
 		       inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
-		       ipv4_l3fwd_route_array[i].depth,
-		       ipv4_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v4[i].depth,
+		       route_base_v4[i].if_out, dev_info.device->name);
 	}
 
 	/* create the LPM6 table */
@@ -608,37 +846,40 @@ setup_lpm(const int socketid)
 	config.flags = 0;
 	ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid,
 				&config);
-	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL) {
+		free_lpm_routes();
 		rte_exit(EXIT_FAILURE,
 			"Unable to create the l3fwd LPM table on socket %d\n",
 			socketid);
+	}
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			free_lpm_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
-		       inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip, abuf,
+		       inet_ntop(AF_INET6, route_base_v6[i].ip_8, abuf,
 				 sizeof(abuf)),
-		       ipv6_l3fwd_route_array[i].depth,
-		       ipv6_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v6[i].depth,
+		       route_base_v6[i].if_out, dev_info.device->name);
 	}
 }
 
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index c7eba06c4d..6d41c926b1 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -14,6 +14,19 @@ struct ipv6_l3fwd_route {
 	uint8_t if_out;
 };
 
-extern const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[16];
+struct lpm_route_rule {
+	union {
+		uint32_t ip;
+		union {
+			uint32_t ip_32[4];
+			uint8_t ip_8[16];
+		};
+	};
+	uint8_t depth;
+	uint8_t if_out;
+};
 
-extern const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[16];
+extern struct lpm_route_rule *route_base_v4;
+extern struct lpm_route_rule *route_base_v6;
+extern int route_num_v4;
+extern int route_num_v6;
diff --git a/examples/l3fwd/lpm_default_v4.cfg b/examples/l3fwd/lpm_default_v4.cfg
new file mode 100644
index 0000000000..4db5597ed8
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD LPM
+R198.18.0.0/24 0
+R198.18.1.0/24 1
+R198.18.2.0/24 2
+R198.18.3.0/24 3
+R198.18.4.0/24 4
+R198.18.5.0/24 5
+R198.18.6.0/24 6
+R198.18.7.0/24 7
+R198.18.8.0/24 8
+R198.18.9.0/24 9
+R198.18.10.0/24 10
+R198.18.11.0/24 11
+R198.18.12.0/24 12
+R198.18.13.0/24 13
+R198.18.14.0/24 14
+R198.18.15.0/24 15
diff --git a/examples/l3fwd/lpm_default_v6.cfg b/examples/l3fwd/lpm_default_v6.cfg
new file mode 100644
index 0000000000..c50233e0ba
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD LPM
+R2001:0200:0000:0000:0000:0000:0000:0000/64 0
+R2001:0200:0000:0001:0000:0000:0000:0000/64 1
+R2001:0200:0000:0002:0000:0000:0000:0000/64 2
+R2001:0200:0000:0003:0000:0000:0000:0000/64 3
+R2001:0200:0000:0004:0000:0000:0000:0000/64 4
+R2001:0200:0000:0005:0000:0000:0000:0000/64 5
+R2001:0200:0000:0006:0000:0000:0000:0000/64 6
+R2001:0200:0000:0007:0000:0000:0000:0000/64 7
+R2001:0200:0000:0008:0000:0000:0000:0000/64 8
+R2001:0200:0000:0009:0000:0000:0000:0000/64 9
+R2001:0200:0000:000A:0000:0000:0000:0000/64 10
+R2001:0200:0000:000B:0000:0000:0000:0000/64 11
+R2001:0200:0000:000C:0000:0000:0000:0000/64 12
+R2001:0200:0000:000D:0000:0000:0000:0000/64 13
+R2001:0200:0000:000E:0000:0000:0000:0000/64 14
+R2001:0200:0000:000F:0000:0000:0000:0000/64 15
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index eb68ffc5aa..927164561e 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -94,6 +94,8 @@ uint32_t hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 
 struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+struct parm_cfg parm_config;
+
 struct lcore_params {
 	uint16_t port_id;
 	uint8_t queue_id;
@@ -141,6 +143,7 @@ static struct rte_mempool *vector_pool[RTE_MAX_ETHPORTS];
 static uint8_t lkp_per_socket[NB_SOCKETS];
 
 struct l3fwd_lkp_mode {
+	void  (*read_config_files)(void);
 	void  (*setup)(int);
 	int   (*check_ptype)(int);
 	rte_rx_callback_fn cb_parse_ptype;
@@ -152,6 +155,7 @@ struct l3fwd_lkp_mode {
 static struct l3fwd_lkp_mode l3fwd_lkp;
 
 static struct l3fwd_lkp_mode l3fwd_em_lkp = {
+	.read_config_files		= read_config_files_em,
 	.setup                  = setup_hash,
 	.check_ptype		= em_check_ptype,
 	.cb_parse_ptype		= em_cb_parse_ptype,
@@ -161,6 +165,7 @@ static struct l3fwd_lkp_mode l3fwd_em_lkp = {
 };
 
 static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_lpm,
 	.check_ptype		= lpm_check_ptype,
 	.cb_parse_ptype		= lpm_cb_parse_ptype,
@@ -170,6 +175,7 @@ static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
 };
 
 static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_fib,
 	.check_ptype            = lpm_check_ptype,
 	.cb_parse_ptype         = lpm_cb_parse_ptype,
@@ -179,50 +185,19 @@ static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
 };
 
 /*
- * 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
- * 198.18.{0-15}.0/24 = Port {0-15}
+ * API's called during initialization to setup ACL/EM/LPM rules.
  */
-const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[] = {
-	{RTE_IPV4(198, 18, 0, 0), 24, 0},
-	{RTE_IPV4(198, 18, 1, 0), 24, 1},
-	{RTE_IPV4(198, 18, 2, 0), 24, 2},
-	{RTE_IPV4(198, 18, 3, 0), 24, 3},
-	{RTE_IPV4(198, 18, 4, 0), 24, 4},
-	{RTE_IPV4(198, 18, 5, 0), 24, 5},
-	{RTE_IPV4(198, 18, 6, 0), 24, 6},
-	{RTE_IPV4(198, 18, 7, 0), 24, 7},
-	{RTE_IPV4(198, 18, 8, 0), 24, 8},
-	{RTE_IPV4(198, 18, 9, 0), 24, 9},
-	{RTE_IPV4(198, 18, 10, 0), 24, 10},
-	{RTE_IPV4(198, 18, 11, 0), 24, 11},
-	{RTE_IPV4(198, 18, 12, 0), 24, 12},
-	{RTE_IPV4(198, 18, 13, 0), 24, 13},
-	{RTE_IPV4(198, 18, 14, 0), 24, 14},
-	{RTE_IPV4(198, 18, 15, 0), 24, 15},
-};
+static void
+l3fwd_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
 
-/*
- * 2001:200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180).
- * 2001:200:0:{0-f}::/64 = Port {0-15}
- */
-const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[] = {
-	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 0},
-	{{32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 1},
-	{{32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 2},
-	{{32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 3},
-	{{32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 4},
-	{{32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 5},
-	{{32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 6},
-	{{32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 7},
-	{{32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 8},
-	{{32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 9},
-	{{32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 10},
-	{{32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 11},
-	{{32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 12},
-	{{32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 13},
-	{{32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 14},
-	{{32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 15},
-};
+static void
+l3fwd_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
 
 /*
  * Setup lookup methods for forwarding.
@@ -339,6 +314,8 @@ print_usage(const char *prgname)
 {
 	fprintf(stderr, "%s [EAL options] --"
 		" -p PORTMASK"
+		"  --rule_ipv4=FILE"
+		"  --rule_ipv6=FILE"
 		" [-P]"
 		" [--lookup]"
 		" --config (port,queue,lcore)[,(port,queue,lcore)]"
@@ -381,7 +358,10 @@ print_usage(const char *prgname)
 		"  --event-vector-size: Max vector size if event vectorization is enabled.\n"
 		"  --event-vector-tmo: Max timeout to form vector in nanoseconds if event vectorization is enabled\n"
 		"  -E : Enable exact match, legacy flag please use --lookup=em instead\n"
-		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n\n",
+		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n"
+		"  --rule_ipv4=FILE: Specify the ipv4 rules entries file.\n"
+		"                    Each rule occupies one line.\n"
+		"  --rule_ipv6=FILE: Specify the ipv6 rules entries file.\n\n",
 		prgname);
 }
 
@@ -596,6 +576,8 @@ static const char short_options[] =
 #define CMD_LINE_OPT_ENABLE_VECTOR "event-vector"
 #define CMD_LINE_OPT_VECTOR_SIZE "event-vector-size"
 #define CMD_LINE_OPT_VECTOR_TMO_NS "event-vector-tmo"
+#define CMD_LINE_OPT_RULE_IPV4 "rule_ipv4"
+#define CMD_LINE_OPT_RULE_IPV6 "rule_ipv6"
 
 enum {
 	/* long options mapped to a short option */
@@ -610,6 +592,8 @@ enum {
 	CMD_LINE_OPT_MAX_PKT_LEN_NUM,
 	CMD_LINE_OPT_HASH_ENTRY_NUM_NUM,
 	CMD_LINE_OPT_PARSE_PTYPE_NUM,
+	CMD_LINE_OPT_RULE_IPV4_NUM,
+	CMD_LINE_OPT_RULE_IPV6_NUM,
 	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
 	CMD_LINE_OPT_MODE_NUM,
 	CMD_LINE_OPT_EVENTQ_SYNC_NUM,
@@ -637,6 +621,8 @@ static const struct option lgopts[] = {
 	{CMD_LINE_OPT_ENABLE_VECTOR, 0, 0, CMD_LINE_OPT_ENABLE_VECTOR_NUM},
 	{CMD_LINE_OPT_VECTOR_SIZE, 1, 0, CMD_LINE_OPT_VECTOR_SIZE_NUM},
 	{CMD_LINE_OPT_VECTOR_TMO_NS, 1, 0, CMD_LINE_OPT_VECTOR_TMO_NS_NUM},
+	{CMD_LINE_OPT_RULE_IPV4,   1, 0, CMD_LINE_OPT_RULE_IPV4_NUM},
+	{CMD_LINE_OPT_RULE_IPV6,   1, 0, CMD_LINE_OPT_RULE_IPV6_NUM},
 	{NULL, 0, 0, 0}
 };
 
@@ -791,6 +777,12 @@ parse_args(int argc, char **argv)
 		case CMD_LINE_OPT_VECTOR_TMO_NS_NUM:
 			evt_rsrc->vector_tmo_ns = strtoull(optarg, NULL, 10);
 			break;
+		case CMD_LINE_OPT_RULE_IPV4_NUM:
+			l3fwd_set_rule_ipv4_name(optarg);
+			break;
+		case CMD_LINE_OPT_RULE_IPV6_NUM:
+			l3fwd_set_rule_ipv6_name(optarg);
+			break;
 		default:
 			print_usage(prgname);
 			return -1;
@@ -1358,6 +1350,24 @@ l3fwd_event_service_setup(void)
 	}
 }
 
+/* Bypass comment and empty lines */
+int
+is_bypass_line(const char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
 int
 main(int argc, char **argv)
 {
@@ -1395,6 +1405,9 @@ main(int argc, char **argv)
 	/* Setup function pointers for lookup method. */
 	setup_l3fwd_lookup_tables();
 
+	/* Add the config file rules */
+	l3fwd_lkp.read_config_files();
+
 	evt_rsrc->per_port_pool = per_port_pool;
 	evt_rsrc->pkt_pool = pktmbuf_pool;
 	evt_rsrc->vec_pool = vector_pool;
-- 
2.25.1


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

* [PATCH v2 2/2] examples/l3fwd: add config file support for EM
  2021-12-20 11:08 ` [PATCH v2 0/2] Add config file support for l3fwd Sean Morrissey
  2021-12-20 11:08   ` [PATCH v2 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
@ 2021-12-20 11:08   ` Sean Morrissey
  2021-12-20 15:53     ` Ananyev, Konstantin
  2021-12-21 12:30   ` [PATCH v3 0/2] Add config file support for l3fwd Sean Morrissey
  2 siblings, 1 reply; 46+ messages in thread
From: Sean Morrissey @ 2021-12-20 11:08 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Ravi Kerur

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for EM with a format
similar to l3fwd-acl one.

With the removal of the hardcoded route tables for IPv4
and IPv6 from 'l3fwd_em', these routes have been moved
to a separate default config file for use with EM.

Related l3fwd docs have been updated to relfect these
changes.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
 examples/l3fwd/em_default_v4.cfg        |  17 +
 examples/l3fwd/em_default_v6.cfg        |  17 +
 examples/l3fwd/l3fwd_em.c               | 473 ++++++++++++++----------
 examples/l3fwd/l3fwd_route.h            |  38 +-
 5 files changed, 412 insertions(+), 222 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg

diff --git a/doc/guides/sample_app_ug/l3_forward.rst b/doc/guides/sample_app_ug/l3_forward.rst
index 6d7d7c5cc1..01d86db95d 100644
--- a/doc/guides/sample_app_ug/l3_forward.rst
+++ b/doc/guides/sample_app_ug/l3_forward.rst
@@ -47,6 +47,7 @@ and loaded into the LPM or FIB object at initialization time.
 In the sample application, hash-based and FIB-based forwarding supports
 both IPv4 and IPv6.
 LPM-based forwarding supports IPv4 only.
+During the initialization phase route rules for IPv4 and IPv6 are read from rule files.
 
 Compiling the Application
 -------------------------
@@ -61,6 +62,8 @@ Running the Application
 The application has a number of command line options::
 
     ./dpdk-l3fwd [EAL options] -- -p PORTMASK
+                             --rule_ipv4=FILE
+                             --rule_ipv6=FILE
                              [-P]
                              [--lookup LOOKUP_METHOD]
                              --config(port,queue,lcore)[,(port,queue,lcore)]
@@ -82,6 +85,11 @@ Where,
 
 * ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
 
+* ``--rule_ipv4=FILE:`` specify the ipv4 rules entries file.
+  Each rule occupies one line.
+
+* ``--rule_ipv6=FILE:`` specify the ipv6 rules entries file.
+
 * ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
   Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
 
@@ -135,7 +143,7 @@ To enable L3 forwarding between two ports, assuming that both ports are in the s
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
+    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)" --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -157,19 +165,23 @@ In this command:
 |          |           |           |                                     |
 +----------+-----------+-----------+-------------------------------------+
 
+*   The -rule_ipv4 option specifies the reading of IPv4 rules sets from the rule_ipv4.cfg file
+
+*   The -rule_ipv6 option specifies the reading of IPv6 rules sets from the rule_ipv6.cfg file.
+
 To use eventdev mode with sync method **ordered** on above mentioned environment,
 Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 or
 
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> \
-		-- -p 0x03 --mode=eventdev --eventq-sched=ordered
+		-- -p 0x03 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -192,7 +204,7 @@ scheduler. Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In case of eventdev mode, *--config* option is not used for ethernet port
 configuration. Instead each ethernet port will be configured with mentioned
@@ -216,6 +228,49 @@ The following sections provide some explanation of the sample application code.
 the initialization and run-time paths are very similar to those of the :doc:`l2_forward_real_virtual` and :doc:`l2_forward_event`.
 The following sections describe aspects that are specific to the L3 Forwarding sample application.
 
+Parse Rules from File
+~~~~~~~~~~~~~~~~~~~~~
+
+The application parses the rules from the file and adds them to the appropriate route table by calling the appropriate function.
+It ignores empty and comment lines, and parses and validates the rules it reads.
+If errors are detected, the application exits with messages to identify the errors encountered.
+
+The format of the route rules differs based on which lookup method is being used.
+Therefore, the code only decreases the priority number with each rule it parses.
+Route rules are mandatory.
+To read data from the specified file successfully, the application assumes the following:
+
+*   Each rule occupies a single line.
+
+*   Only the following four rule line types are valid in this application:
+
+*   Route rule line, which starts with a leading character 'R'
+
+*   Comment line, which starts with a leading character '#'
+
+*   Empty line, which consists of a space, form-feed ('\f'), newline ('\n'),
+    carriage return ('\r'), horizontal tab ('\t'), or vertical tab ('\v').
+
+Other lines types are considered invalid.
+
+*   Rules are organized in descending order of priority,
+    which means rules at the head of the file always have a higher priority than those further down in the file.
+
+*   A typical IPv4 LPM/FIB rule line should have a format as shown below:
+
+R<destination_ip>/<ip_mask_length><output_port_number>
+
+*   A typical IPv4 EM rule line should have a format as shown below:
+
+R<destination_ip><source_ip><destination_port><source_port><protocol><output_port_number>
+
+IPv4 addresses are specified in CIDR format as specified in RFC 4632.
+For LPM/FIB they consist of the dot notation for the address and a prefix length separated by '/'.
+For example, 192.168.0.34/32, where the address is 192.168.0.34 and the prefix length is 32.
+For EM they consist of just the dot notation for the address and no prefix length.
+For example, 192.168.0.34, where the Address is 192.168.0.34.
+EM also includes ports which are specified as a single number which represents a single port.
+
 Hash Initialization
 ~~~~~~~~~~~~~~~~~~~
 
@@ -227,8 +282,6 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
 
     The Hash initialization will setup both ipv4 and ipv6 hash table,
     and populate the either table depending on the value of variable ipv6.
-    To support the hash performance test with up to 8M single direction flows/16M bi-direction flows,
-    populate_ipv4_many_flow_into_table() function will populate the hash table with specified hash table entry number(default 4M).
 
 .. note::
 
@@ -246,22 +299,14 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
         {
             // ...
 
-            if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_many_flow_into_table(ipv4_l3fwd_lookup_struct[socketid], hash_entry_number);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_many_flow_into_table( ipv6_l3fwd_lookup_struct[socketid], hash_entry_number);
-                }
-            } else
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_few_flow_into_table(ipv4_l3fwd_lookup_struct[socketid]);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_few_flow_into_table(ipv6_l3fwd_lookup_struct[socketid]);
-                }
+            if (ipv6 == 0) {
+                /* populate the ipv4 hash */
+                populate_ipv4_flow_into_table(
+                    ipv4_l3fwd_em_lookup_struct[socketid]);
+            } else {
+                /* populate the ipv6 hash */
+                populate_ipv6_flow_into_table(
+                    ipv6_l3fwd_em_lookup_struct[socketid]);
             }
         }
     #endif
diff --git a/examples/l3fwd/em_default_v4.cfg b/examples/l3fwd/em_default_v4.cfg
new file mode 100644
index 0000000000..b70bdb6601
--- /dev/null
+++ b/examples/l3fwd/em_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD EM
+R198.18.0.0 198.18.0.1 9 9 0x11 0
+R198.18.1.0 198.18.1.1 9 9 0x11 1
+R198.18.2.0 198.18.2.1 9 9 0x11 2
+R198.18.3.0 198.18.3.1 9 9 0x11 3
+R198.18.4.0 198.18.4.1 9 9 0x11 4
+R198.18.5.0 198.18.5.1 9 9 0x11 5
+R198.18.6.0 198.18.6.1 9 9 0x11 6
+R198.18.7.0 198.18.7.1 9 9 0x11 7
+R198.18.8.0 198.18.8.1 9 9 0x11 8
+R198.18.9.0 198.18.9.1 9 9 0x11 9
+R198.18.10.0 198.18.10.1 9 9 0x11 10
+R198.18.11.0 198.18.11.1 9 9 0x11 11
+R198.18.12.0 198.18.12.1 9 9 0x11 12
+R198.18.13.0 198.18.13.1 9 9 0x11 13
+R198.18.14.0 198.18.14.1 9 9 0x11 14
+R198.18.15.0 198.18.15.1 9 9 0x11 15
diff --git a/examples/l3fwd/em_default_v6.cfg b/examples/l3fwd/em_default_v6.cfg
new file mode 100644
index 0000000000..0b94f8d8d2
--- /dev/null
+++ b/examples/l3fwd/em_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD EM
+R2001:0200:0000:0000:0000:0000:0000:0000 2001:0200:0000:0000:0000:0000:0000:0001 9 9 0x11 0
+R2001:0200:0000:0001:0000:0000:0000:0000 2001:0200:0000:0001:0000:0000:0000:0001 9 9 0x11 1
+R2001:0200:0000:0002:0000:0000:0000:0000 2001:0200:0000:0002:0000:0000:0000:0001 9 9 0x11 2
+R2001:0200:0000:0003:0000:0000:0000:0000 2001:0200:0000:0003:0000:0000:0000:0001 9 9 0x11 3
+R2001:0200:0000:0004:0000:0000:0000:0000 2001:0200:0000:0004:0000:0000:0000:0001 9 9 0x11 4
+R2001:0200:0000:0005:0000:0000:0000:0000 2001:0200:0000:0005:0000:0000:0000:0001 9 9 0x11 5
+R2001:0200:0000:0006:0000:0000:0000:0000 2001:0200:0000:0006:0000:0000:0000:0001 9 9 0x11 6
+R2001:0200:0000:0007:0000:0000:0000:0000 2001:0200:0000:0007:0000:0000:0000:0001 9 9 0x11 7
+R2001:0200:0000:0008:0000:0000:0000:0000 2001:0200:0000:0008:0000:0000:0000:0001 9 9 0x11 8
+R2001:0200:0000:0009:0000:0000:0000:0000 2001:0200:0000:0009:0000:0000:0000:0001 9 9 0x11 9
+R2001:0200:0000:000A:0000:0000:0000:0000 2001:0200:0000:000A:0000:0000:0000:0001 9 9 0x11 10
+R2001:0200:0000:000B:0000:0000:0000:0000 2001:0200:0000:000B:0000:0000:0000:0001 9 9 0x11 11
+R2001:0200:0000:000C:0000:0000:0000:0000 2001:0200:0000:000C:0000:0000:0000:0001 9 9 0x11 12
+R2001:0200:0000:000D:0000:0000:0000:0000 2001:0200:0000:000D:0000:0000:0000:0001 9 9 0x11 13
+R2001:0200:0000:000E:0000:0000:0000:0000 2001:0200:0000:000E:0000:0000:0000:0001 9 9 0x11 14
+R2001:0200:0000:000F:0000:0000:0000:0000 2001:0200:0000:000F:0000:0000:0000:0001 9 9 0x11 15
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 4953cdae4e..9a337ed842 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -27,6 +27,7 @@
 
 #include "l3fwd.h"
 #include "l3fwd_event.h"
+#include "l3fwd_route.h"
 
 #if defined(RTE_ARCH_X86) || defined(__ARM_FEATURE_CRC32)
 #define EM_HASH_CRC 1
@@ -42,13 +43,18 @@
 
 #define IPV6_ADDR_LEN 16
 
-struct ipv4_5tuple {
-	uint32_t ip_dst;
-	uint32_t ip_src;
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
+static struct em_rule *em_route_base_v4;
+static struct em_rule *em_route_base_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_SRC_ADDR,
+	CB_FLD_DST_PORT,
+	CB_FLD_SRC_PORT,
+	CB_FLD_PROTO,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
 
 union ipv4_5tuple_host {
 	struct {
@@ -65,14 +71,6 @@ union ipv4_5tuple_host {
 
 #define XMM_NUM_IN_IPV6_5TUPLE 3
 
-struct ipv6_5tuple {
-	uint8_t  ip_dst[IPV6_ADDR_LEN];
-	uint8_t  ip_src[IPV6_ADDR_LEN];
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
-
 union ipv6_5tuple_host {
 	struct {
 		uint16_t pad0;
@@ -87,8 +85,6 @@ union ipv6_5tuple_host {
 	xmm_t xmm[XMM_NUM_IN_IPV6_5TUPLE];
 };
 
-
-
 struct ipv4_l3fwd_em_route {
 	struct ipv4_5tuple key;
 	uint8_t if_out;
@@ -99,66 +95,6 @@ struct ipv6_l3fwd_em_route {
 	uint8_t if_out;
 };
 
-/* 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
- * Use RFC863 Discard Protocol.
- */
-static const struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[] = {
-	{{RTE_IPV4(198, 18, 0, 0), RTE_IPV4(198, 18, 0, 1),  9, 9, IPPROTO_UDP}, 0},
-	{{RTE_IPV4(198, 18, 1, 0), RTE_IPV4(198, 18, 1, 1),  9, 9, IPPROTO_UDP}, 1},
-	{{RTE_IPV4(198, 18, 2, 0), RTE_IPV4(198, 18, 2, 1),  9, 9, IPPROTO_UDP}, 2},
-	{{RTE_IPV4(198, 18, 3, 0), RTE_IPV4(198, 18, 3, 1),  9, 9, IPPROTO_UDP}, 3},
-	{{RTE_IPV4(198, 18, 4, 0), RTE_IPV4(198, 18, 4, 1),  9, 9, IPPROTO_UDP}, 4},
-	{{RTE_IPV4(198, 18, 5, 0), RTE_IPV4(198, 18, 5, 1),  9, 9, IPPROTO_UDP}, 5},
-	{{RTE_IPV4(198, 18, 6, 0), RTE_IPV4(198, 18, 6, 1),  9, 9, IPPROTO_UDP}, 6},
-	{{RTE_IPV4(198, 18, 7, 0), RTE_IPV4(198, 18, 7, 1),  9, 9, IPPROTO_UDP}, 7},
-	{{RTE_IPV4(198, 18, 8, 0), RTE_IPV4(198, 18, 8, 1),  9, 9, IPPROTO_UDP}, 8},
-	{{RTE_IPV4(198, 18, 9, 0), RTE_IPV4(198, 18, 9, 1),  9, 9, IPPROTO_UDP}, 9},
-	{{RTE_IPV4(198, 18, 10, 0), RTE_IPV4(198, 18, 10, 1),  9, 9, IPPROTO_UDP}, 10},
-	{{RTE_IPV4(198, 18, 11, 0), RTE_IPV4(198, 18, 11, 1),  9, 9, IPPROTO_UDP}, 11},
-	{{RTE_IPV4(198, 18, 12, 0), RTE_IPV4(198, 18, 12, 1),  9, 9, IPPROTO_UDP}, 12},
-	{{RTE_IPV4(198, 18, 13, 0), RTE_IPV4(198, 18, 13, 1),  9, 9, IPPROTO_UDP}, 13},
-	{{RTE_IPV4(198, 18, 14, 0), RTE_IPV4(198, 18, 14, 1),  9, 9, IPPROTO_UDP}, 14},
-	{{RTE_IPV4(198, 18, 15, 0), RTE_IPV4(198, 18, 15, 1),  9, 9, IPPROTO_UDP}, 15},
-};
-
-/* 2001:0200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180).
- * Use RFC863 Discard Protocol.
- */
-static const struct ipv6_l3fwd_em_route ipv6_l3fwd_em_route_array[] = {
-	{{{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 0},
-	{{{32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 1},
-	{{{32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 2},
-	{{{32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 3},
-	{{{32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 4},
-	{{{32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 5},
-	{{{32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 6},
-	{{{32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 7},
-	{{{32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 8},
-	{{{32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 9},
-	{{{32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 10},
-	{{{32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 11},
-	{{{32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 12},
-	{{{32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 13},
-	{{{32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 14},
-	{{{32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 15},
-};
-
 struct rte_hash *ipv4_l3fwd_em_lookup_struct[NB_SOCKETS];
 struct rte_hash *ipv6_l3fwd_em_lookup_struct[NB_SOCKETS];
 
@@ -235,10 +171,6 @@ ipv6_hash_crc(const void *data, __rte_unused uint32_t data_len,
 	return init_val;
 }
 
-#define IPV4_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv4_l3fwd_em_route_array)
-
-#define IPV6_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv6_l3fwd_em_route_array)
-
 static uint8_t ipv4_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 static uint8_t ipv6_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 
@@ -346,6 +278,178 @@ em_get_ipv6_dst_port(void *ipv6_hdr, uint16_t portid, void *lookup_struct)
 #include "l3fwd_em.h"
 #endif
 
+static int
+em_parse_v6_net(const char *in, uint32_t *v)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = inet_pton(AF_INET6, in, &v);
+	if (rc != 1)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+em_parse_v6_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	uint32_t ip[4];
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	ip[0] = *(v->v6_key.ip32_dst);
+	rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], ip);
+	if (rc != 0)
+		return rc;
+	ip[0] = *(v->v6_key.ip32_src);
+	rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], ip);
+	if (rc != 0)
+		return rc;
+
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_parse_v4_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = inet_pton(AF_INET, in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
+	v->v4_key.ip_dst = ntohl(v->v4_key.ip_dst);
+	if (rc != 1)
+		return rc;
+
+	rc = inet_pton(AF_INET, in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
+	v->v4_key.ip_src = ntohl(v->v4_key.ip_src);
+	if (rc != 1)
+		return rc;
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_add_rules(const char *rule_path,
+		struct em_rule **proute_base,
+		int (*parser)(char *, struct em_rule *))
+{
+	struct em_rule *route_rules;
+	struct em_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
 static void
 convert_ipv4_5tuple(struct ipv4_5tuple *key1,
 		union ipv4_5tuple_host *key2)
@@ -382,122 +486,92 @@ convert_ipv6_5tuple(struct ipv6_5tuple *key1,
 #define BIT_8_TO_15 0x0000ff00
 
 static inline void
-populate_ipv4_few_flow_into_table(const struct rte_hash *h)
+populate_ipv4_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
-	for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv4_l3fwd_em_route  entry;
+	for (i = 0; i < route_num_v4; i++) {
+		struct em_rule *entry;
 		union ipv4_5tuple_host newkey;
+		struct in_addr src;
+		struct in_addr dst;
 
-		entry = ipv4_l3fwd_em_route_array[i];
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v4[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v4[i];
+		convert_ipv4_5tuple(&(entry->v4_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv4_l3fwd_out_if[ret] = entry.if_out;
+		ipv4_l3fwd_out_if[ret] = entry->if_out;
+		rte_eth_dev_info_get(em_route_base_v4[i].if_out,
+				     &dev_info);
+
+		src.s_addr = htonl(em_route_base_v4[i].v4_key.ip_src);
+		dst.s_addr = htonl(em_route_base_v4[i].v4_key.ip_dst);
+		printf("EM: Adding route %s %s (%d) [%s]\n",
+			   inet_ntop(AF_INET, &dst, dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET, &src, srcbuf, sizeof(srcbuf)),
+		       em_route_base_v4[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 " keys\n",
-		(uint64_t)IPV4_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v4);
 }
 
 #define BIT_16_TO_23 0x00ff0000
 static inline void
-populate_ipv6_few_flow_into_table(const struct rte_hash *h)
+populate_ipv6_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
-	for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv6_l3fwd_em_route entry;
+	for (i = 0; i < route_num_v6; i++) {
+		struct em_rule *entry;
 		union ipv6_5tuple_host newkey;
 
-		entry = ipv6_l3fwd_em_route_array[i];
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v6[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v6[i];
+		convert_ipv6_5tuple(&(entry->v6_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv6_l3fwd_out_if[ret] = entry.if_out;
+		ipv6_l3fwd_out_if[ret] = entry->if_out;
+		rte_eth_dev_info_get(em_route_base_v6[i].if_out,
+				     &dev_info);
+
+		printf("EM: Adding route %s %s (%d) [%s]\n",
+			   inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip32_dst,
+			   dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip32_src,
+			   srcbuf, sizeof(srcbuf)),
+		       em_route_base_v6[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 "keys\n",
-		(uint64_t)IPV6_L3FWD_EM_NUM_ROUTES);
-}
-
-#define NUMBER_PORT_USED 16
-static inline void
-populate_ipv4_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv4_l3fwd_em_route entry;
-		union ipv4_5tuple_host newkey;
-
-		uint8_t a = (uint8_t)((port + 1) % BYTE_VALUE_MAX);
-
-		/* Create the ipv4 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv4_l3fwd_em_route_array[port];
-		entry.key.ip_dst = RTE_IPV4(198, 18, port, a);
-		convert_ipv4_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv4_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
-}
-
-static inline void
-populate_ipv6_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv6_l3fwd_em_route entry;
-		union ipv6_5tuple_host newkey;
-
-		/* Create the ipv6 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv6_l3fwd_em_route_array[port];
-		entry.key.ip_dst[15] = (port + 1) % BYTE_VALUE_MAX;
-		convert_ipv6_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv6_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
+		(uint64_t)route_num_v6);
 }
 
 /* Requirements:
@@ -972,17 +1046,53 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+static void
+free_em_routes(void)
+{
+	free(em_route_base_v4);
+	free(em_route_base_v6);
+	em_route_base_v4 = NULL;
+	em_route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
+}
+
 /* Load rules from the input file */
 void
 read_config_files_em(void)
 {
-	/* Empty till config file support added to EM */
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL) {
+		route_num_v4 = em_add_rules(parm_config.rule_ipv4_name,
+					&em_route_base_v4, &em_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			free_em_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv4 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "EM IPv4 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+	}
+
+	/* ipv6 check */
+	if (parm_config.rule_ipv6_name != NULL) {
+		route_num_v6 = em_add_rules(parm_config.rule_ipv6_name,
+					&em_route_base_v6, &em_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			free_em_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "EM IPv6 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+	}
 }
 
 /* Initialize exact match (hash) parameters. 8< */
 void
 setup_hash(const int socketid)
 {
+	printf("IPPPROTO_UDP: %d\n", IPPROTO_UDP);
 	struct rte_hash_parameters ipv4_l3fwd_hash_params = {
 		.name = NULL,
 		.entries = L3FWD_HASH_ENTRIES,
@@ -1023,35 +1133,18 @@ setup_hash(const int socketid)
 			"Unable to create the l3fwd hash on socket %d\n",
 			socketid);
 
-	if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-		/* For testing hash matching with a large number of flows we
-		 * generate millions of IP 5-tuples with an incremented dst
-		 * address to initialize the hash table. */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_many_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_many_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		}
+	/*
+	 * Use data from ipv4/ipv6 l3fwd config file
+	 * directly to initialize the hash table.
+	 */
+	if (ipv6 == 0) {
+		/* populate the ipv4 hash */
+		populate_ipv4_flow_into_table(
+			ipv4_l3fwd_em_lookup_struct[socketid]);
 	} else {
-		/*
-		 * Use data in ipv4/ipv6 l3fwd lookup table
-		 * directly to initialize the hash table.
-		 */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_few_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid]);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_few_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid]);
-		}
+		/* populate the ipv6 hash */
+		populate_ipv6_flow_into_table(
+			ipv6_l3fwd_em_lookup_struct[socketid]);
 	}
 }
 /* >8 End of initialization of hash parameters. */
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index 6d41c926b1..a3ac991407 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -2,17 +2,27 @@
  * Copyright(c) 2021 Intel Corporation
  */
 
-struct ipv4_l3fwd_route {
-	uint32_t ip;
-	uint8_t  depth;
-	uint8_t  if_out;
-};
+struct ipv4_5tuple {
+	uint32_t ip_dst;
+	uint32_t ip_src;
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
 
-struct ipv6_l3fwd_route {
-	uint8_t ip[16];
-	uint8_t depth;
-	uint8_t if_out;
-};
+struct ipv6_5tuple {
+	union {
+		uint8_t  ip_dst[IPV6_ADDR_LEN];
+		uint32_t ip32_dst[4];
+	};
+	union {
+		uint8_t  ip_src[IPV6_ADDR_LEN];
+		uint32_t ip32_src[4];
+	};
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
 
 struct lpm_route_rule {
 	union {
@@ -26,6 +36,14 @@ struct lpm_route_rule {
 	uint8_t if_out;
 };
 
+struct em_rule {
+		union {
+		struct ipv4_5tuple v4_key;
+		struct ipv6_5tuple v6_key;
+	};
+	uint8_t if_out;
+};
+
 extern struct lpm_route_rule *route_base_v4;
 extern struct lpm_route_rule *route_base_v6;
 extern int route_num_v4;
-- 
2.25.1


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

* RE: [PATCH v2 1/2] examples/l3fwd: add config file support for LPM/FIB
  2021-12-20 11:08   ` [PATCH v2 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
@ 2021-12-20 15:42     ` Ananyev, Konstantin
  0 siblings, 0 replies; 46+ messages in thread
From: Ananyev, Konstantin @ 2021-12-20 15:42 UTC (permalink / raw)
  To: Morrissey, Sean; +Cc: dev, Morrissey, Sean, Ravi Kerur

> Add support to define ipv4 and ipv6 forwarding tables
> from reading from a config file for LPM and FIB,
> with format similar to l3fwd-acl one.
> 
> With the removal of the hardcoded route tables for IPv4
> and IPv6, these routes have been moved to a separate
> default config file for use with LPM and FIB.
> 
> Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
> Signed-off-by: Ravi Kerur <rkerur@gmail.com>
> ---

Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

> --
> 2.25.1


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

* RE: [PATCH v2 2/2] examples/l3fwd: add config file support for EM
  2021-12-20 11:08   ` [PATCH v2 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
@ 2021-12-20 15:53     ` Ananyev, Konstantin
  0 siblings, 0 replies; 46+ messages in thread
From: Ananyev, Konstantin @ 2021-12-20 15:53 UTC (permalink / raw)
  To: Morrissey, Sean; +Cc: dev, Morrissey, Sean, Ravi Kerur


Hi Sean,

Few comments below.

> Add support to define ipv4 and ipv6 forwarding tables
> from reading from a config file for EM with a format
> similar to l3fwd-acl one.
> 
> With the removal of the hardcoded route tables for IPv4
> and IPv6 from 'l3fwd_em', these routes have been moved
> to a separate default config file for use with EM.
> 
> Related l3fwd docs have been updated to relfect these
> changes.
> 
> Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
> Signed-off-by: Ravi Kerur <rkerur@gmail.com>
> ---
>  doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
>  examples/l3fwd/em_default_v4.cfg        |  17 +
>  examples/l3fwd/em_default_v6.cfg        |  17 +
>  examples/l3fwd/l3fwd_em.c               | 473 ++++++++++++++----------
>  examples/l3fwd/l3fwd_route.h            |  38 +-
>  5 files changed, 412 insertions(+), 222 deletions(-)
>  create mode 100644 examples/l3fwd/em_default_v4.cfg
>  create mode 100644 examples/l3fwd/em_default_v6.cfg
> 

...

> diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c

....

> @@ -346,6 +278,178 @@ em_get_ipv6_dst_port(void *ipv6_hdr, uint16_t portid, void *lookup_struct)
>  #include "l3fwd_em.h"
>  #endif
> 
> +static int
> +em_parse_v6_net(const char *in, uint32_t *v)
> +{
> +	int32_t rc;
> +
> +	/* get address. */
> +	rc = inet_pton(AF_INET6, in, &v);

Why '&v'?
I believe it should be:
rc = inet_pton(AF_INET6, in, v);
here.

> +	if (rc != 1)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int
> +em_parse_v6_rule(char *str, struct em_rule *v)
> +{
> +	int i, rc;
> +	uint32_t ip[4];
> +	char *s, *sp, *in[CB_FLD_MAX];
> +	static const char *dlm = " \t\n";
> +	int dim = CB_FLD_MAX;
> +	s = str;
> +
> +	for (i = 0; i != dim; i++, s = NULL) {
> +		in[i] = strtok_r(s, dlm, &sp);
> +		if (in[i] == NULL)
> +			return -EINVAL;
> +	}
> +
> +	ip[0] = *(v->v6_key.ip32_dst);
> +	rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], ip);

Doesn't look right to me.
I think it should be just:
rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], v->v6_key.ip32_dst);
and we don't need local 'ip[]' at all. 


> +	if (rc != 0)
> +		return rc;
> +	ip[0] = *(v->v6_key.ip32_src);
> +	rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], ip);

Same:
rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], v->v6_key.ip32_src);

> +	if (rc != 0)
> +		return rc;
> +
> +
> +	/* source port. */
> +	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
> +	/* destination port. */
> +	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
> +	/* protocol. */
> +	GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
> +	/* out interface. */
> +	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
> +
> +	return 0;
> +}
> +
> +static int
> +em_parse_v4_rule(char *str, struct em_rule *v)
> +{
> +	int i, rc;
> +	char *s, *sp, *in[CB_FLD_MAX];
> +	static const char *dlm = " \t\n";
> +	int dim = CB_FLD_MAX;
> +	s = str;
> +
> +	for (i = 0; i != dim; i++, s = NULL) {
> +		in[i] = strtok_r(s, dlm, &sp);
> +		if (in[i] == NULL)
> +			return -EINVAL;
> +	}
> +
> +	rc = inet_pton(AF_INET, in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
> +	v->v4_key.ip_dst = ntohl(v->v4_key.ip_dst);
> +	if (rc != 1)
> +		return rc;
> +
> +	rc = inet_pton(AF_INET, in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
> +	v->v4_key.ip_src = ntohl(v->v4_key.ip_src);
> +	if (rc != 1)
> +		return rc;
> +
> +	/* source port. */
> +	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
> +	/* destination port. */
> +	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
> +	/* protocol. */
> +	GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
> +	/* out interface. */
> +	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
> +
> +	return 0;
> +}
> +
> +static int
> +em_add_rules(const char *rule_path,
> +		struct em_rule **proute_base,
> +		int (*parser)(char *, struct em_rule *))
> +{
> +	struct em_rule *route_rules;
> +	struct em_rule *next;
> +	unsigned int route_num = 0;
> +	unsigned int route_cnt = 0;
> +	char buff[LINE_MAX];
> +	FILE *fh;
> +	unsigned int i = 0, rule_size = sizeof(*next);
> +	int val;
> +
> +	*proute_base = NULL;
> +	fh = fopen(rule_path, "rb");
> +	if (fh == NULL)
> +		return -EINVAL;
> +
> +	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
> +		if (buff[0] == ROUTE_LEAD_CHAR)
> +			route_num++;
> +	}
> +
> +	if (route_num == 0) {
> +		fclose(fh);
> +		return -EINVAL;
> +	}
> +
> +	val = fseek(fh, 0, SEEK_SET);
> +	if (val < 0) {
> +		fclose(fh);
> +		return -EINVAL;
> +	}
> +
> +	route_rules = calloc(route_num, rule_size);
> +
> +	if (route_rules == NULL) {
> +		fclose(fh);
> +		return -EINVAL;
> +	}
> +
> +	i = 0;
> +	while (fgets(buff, LINE_MAX, fh) != NULL) {
> +		i++;
> +		if (is_bypass_line(buff))
> +			continue;
> +
> +		char s = buff[0];
> +
> +		/* Route entry */
> +		if (s == ROUTE_LEAD_CHAR)
> +			next = &route_rules[route_cnt];
> +
> +		/* Illegal line */
> +		else {
> +			RTE_LOG(ERR, L3FWD,
> +				"%s Line %u: should start with leading "
> +				"char %c\n",
> +				rule_path, i, ROUTE_LEAD_CHAR);
> +			fclose(fh);
> +			free(route_rules);
> +			return -EINVAL;
> +		}
> +
> +		if (parser(buff + 1, next) != 0) {
> +			RTE_LOG(ERR, L3FWD,
> +				"%s Line %u: parse rules error\n",
> +				rule_path, i);
> +			fclose(fh);
> +			free(route_rules);
> +			return -EINVAL;
> +		}
> +
> +		route_cnt++;
> +	}
> +
> +	fclose(fh);
> +
> +	*proute_base = route_rules;
> +
> +	return route_cnt;
> +}
> +
>  static void
>  convert_ipv4_5tuple(struct ipv4_5tuple *key1,
>  		union ipv4_5tuple_host *key2)
> @@ -382,122 +486,92 @@ convert_ipv6_5tuple(struct ipv6_5tuple *key1,
>  #define BIT_8_TO_15 0x0000ff00
> 
>  static inline void
> -populate_ipv4_few_flow_into_table(const struct rte_hash *h)
> +populate_ipv4_flow_into_table(const struct rte_hash *h)
>  {
> -	uint32_t i;
> +	int i;
>  	int32_t ret;
> +	struct rte_eth_dev_info dev_info;
> +	char srcbuf[INET6_ADDRSTRLEN];
> +	char dstbuf[INET6_ADDRSTRLEN];
> 
>  	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
>  				ALL_32_BITS, ALL_32_BITS} };
> 
> -	for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) {
> -		struct ipv4_l3fwd_em_route  entry;
> +	for (i = 0; i < route_num_v4; i++) {
> +		struct em_rule *entry;
>  		union ipv4_5tuple_host newkey;
> +		struct in_addr src;
> +		struct in_addr dst;
> 
> -		entry = ipv4_l3fwd_em_route_array[i];
> -		convert_ipv4_5tuple(&entry.key, &newkey);
> +		if ((1 << em_route_base_v4[i].if_out &
> +				enabled_port_mask) == 0)
> +			continue;
> +
> +		entry = &em_route_base_v4[i];
> +		convert_ipv4_5tuple(&(entry->v4_key), &newkey);
>  		ret = rte_hash_add_key(h, (void *) &newkey);
>  		if (ret < 0) {
>  			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
>  				" to the l3fwd hash.\n", i);
>  		}
> -		ipv4_l3fwd_out_if[ret] = entry.if_out;
> +		ipv4_l3fwd_out_if[ret] = entry->if_out;
> +		rte_eth_dev_info_get(em_route_base_v4[i].if_out,
> +				     &dev_info);
> +
> +		src.s_addr = htonl(em_route_base_v4[i].v4_key.ip_src);
> +		dst.s_addr = htonl(em_route_base_v4[i].v4_key.ip_dst);
> +		printf("EM: Adding route %s %s (%d) [%s]\n",
> +			   inet_ntop(AF_INET, &dst, dstbuf, sizeof(dstbuf)),
> +		       inet_ntop(AF_INET, &src, srcbuf, sizeof(srcbuf)),
> +		       em_route_base_v4[i].if_out, dev_info.device->name);
>  	}
>  	printf("Hash: Adding 0x%" PRIx64 " keys\n",
> -		(uint64_t)IPV4_L3FWD_EM_NUM_ROUTES);
> +		(uint64_t)route_num_v4);
>  }
> 
>  #define BIT_16_TO_23 0x00ff0000
>  static inline void
> -populate_ipv6_few_flow_into_table(const struct rte_hash *h)
> +populate_ipv6_flow_into_table(const struct rte_hash *h)
>  {
> -	uint32_t i;
> +	int i;
>  	int32_t ret;
> +	struct rte_eth_dev_info dev_info;
> +	char srcbuf[INET6_ADDRSTRLEN];
> +	char dstbuf[INET6_ADDRSTRLEN];
> 
>  	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
>  				ALL_32_BITS, ALL_32_BITS} };
> 
>  	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
> 
> -	for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) {
> -		struct ipv6_l3fwd_em_route entry;
> +	for (i = 0; i < route_num_v6; i++) {
> +		struct em_rule *entry;
>  		union ipv6_5tuple_host newkey;
> 
> -		entry = ipv6_l3fwd_em_route_array[i];
> -		convert_ipv6_5tuple(&entry.key, &newkey);
> +		if ((1 << em_route_base_v6[i].if_out &
> +				enabled_port_mask) == 0)
> +			continue;
> +
> +		entry = &em_route_base_v6[i];
> +		convert_ipv6_5tuple(&(entry->v6_key), &newkey);
>  		ret = rte_hash_add_key(h, (void *) &newkey);
>  		if (ret < 0) {
>  			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
>  				" to the l3fwd hash.\n", i);
>  		}
> -		ipv6_l3fwd_out_if[ret] = entry.if_out;
> +		ipv6_l3fwd_out_if[ret] = entry->if_out;
> +		rte_eth_dev_info_get(em_route_base_v6[i].if_out,
> +				     &dev_info);
> +
> +		printf("EM: Adding route %s %s (%d) [%s]\n",
> +			   inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip32_dst,
> +			   dstbuf, sizeof(dstbuf)),
> +		       inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip32_src,
> +			   srcbuf, sizeof(srcbuf)),
> +		       em_route_base_v6[i].if_out, dev_info.device->name);

I think we need to print full key here: <dest_ip,src_ip, dst_port, src_port, proro>.
Otherwise it is sort of misleading.
Same for ipv4.


>  	}
>  	printf("Hash: Adding 0x%" PRIx64 "keys\n",
> -		(uint64_t)IPV6_L3FWD_EM_NUM_ROUTES);
> -}
> -
> -#define NUMBER_PORT_USED 16
> -static inline void
> -populate_ipv4_many_flow_into_table(const struct rte_hash *h,
> -		unsigned int nr_flow)
> -{
> -	unsigned i;
> -
> -	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
> -				ALL_32_BITS, ALL_32_BITS} };
> -
> -	for (i = 0; i < nr_flow; i++) {
> -		uint8_t port = i % NUMBER_PORT_USED;
> -		struct ipv4_l3fwd_em_route entry;
> -		union ipv4_5tuple_host newkey;
> -
> -		uint8_t a = (uint8_t)((port + 1) % BYTE_VALUE_MAX);
> -
> -		/* Create the ipv4 exact match flow */
> -		memset(&entry, 0, sizeof(entry));
> -		entry = ipv4_l3fwd_em_route_array[port];
> -		entry.key.ip_dst = RTE_IPV4(198, 18, port, a);
> -		convert_ipv4_5tuple(&entry.key, &newkey);
> -		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
> -
> -		if (ret < 0)
> -			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
> -
> -		ipv4_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
> -
> -	}
> -	printf("Hash: Adding 0x%x keys\n", nr_flow);
> -}
> -
> -static inline void
> -populate_ipv6_many_flow_into_table(const struct rte_hash *h,
> -		unsigned int nr_flow)
> -{
> -	unsigned i;
> -
> -	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
> -				ALL_32_BITS, ALL_32_BITS} };
> -	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
> -
> -	for (i = 0; i < nr_flow; i++) {
> -		uint8_t port = i % NUMBER_PORT_USED;
> -		struct ipv6_l3fwd_em_route entry;
> -		union ipv6_5tuple_host newkey;
> -
> -		/* Create the ipv6 exact match flow */
> -		memset(&entry, 0, sizeof(entry));
> -		entry = ipv6_l3fwd_em_route_array[port];
> -		entry.key.ip_dst[15] = (port + 1) % BYTE_VALUE_MAX;
> -		convert_ipv6_5tuple(&entry.key, &newkey);
> -		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
> -
> -		if (ret < 0)
> -			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
> -
> -		ipv6_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
> -
> -	}
> -	printf("Hash: Adding 0x%x keys\n", nr_flow);
> +		(uint64_t)route_num_v6);
>  }
> 
>  /* Requirements:
> @@ -972,17 +1046,53 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
>  	return 0;
>  }
> 
> +static void
> +free_em_routes(void)
> +{
> +	free(em_route_base_v4);
> +	free(em_route_base_v6);
> +	em_route_base_v4 = NULL;
> +	em_route_base_v6 = NULL;
> +	route_num_v4 = 0;
> +	route_num_v6 = 0;
> +}
> +
>  /* Load rules from the input file */
>  void
>  read_config_files_em(void)
>  {
> -	/* Empty till config file support added to EM */
> +	/* ipv4 check */
> +	if (parm_config.rule_ipv4_name != NULL) {
> +		route_num_v4 = em_add_rules(parm_config.rule_ipv4_name,
> +					&em_route_base_v4, &em_parse_v4_rule);
> +		if (route_num_v4 < 0) {
> +			free_em_routes();
> +			rte_exit(EXIT_FAILURE, "Failed to add EM IPv4 rules\n");
> +		}
> +	} else {
> +		RTE_LOG(ERR, L3FWD, "EM IPv4 rule file not specified\n");
> +		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
> +	}
> +
> +	/* ipv6 check */
> +	if (parm_config.rule_ipv6_name != NULL) {
> +		route_num_v6 = em_add_rules(parm_config.rule_ipv6_name,
> +					&em_route_base_v6, &em_parse_v6_rule);
> +		if (route_num_v6 < 0) {
> +			free_em_routes();
> +			rte_exit(EXIT_FAILURE, "Failed to add EM IPv6 rules\n");
> +		}
> +	} else {
> +		RTE_LOG(ERR, L3FWD, "EM IPv6 rule file not specified\n");
> +		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
> +	}
>  }
> 
>  /* Initialize exact match (hash) parameters. 8< */
>  void
>  setup_hash(const int socketid)
>  {
> +	printf("IPPPROTO_UDP: %d\n", IPPROTO_UDP);

Looks unrelated, pls remove.

>  	struct rte_hash_parameters ipv4_l3fwd_hash_params = {
>  		.name = NULL,
>  		.entries = L3FWD_HASH_ENTRIES,
> @@ -1023,35 +1133,18 @@ setup_hash(const int socketid)
>  			"Unable to create the l3fwd hash on socket %d\n",
>  			socketid);
> 

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

* [PATCH v3 0/2] Add config file support for l3fwd
  2021-12-20 11:08 ` [PATCH v2 0/2] Add config file support for l3fwd Sean Morrissey
  2021-12-20 11:08   ` [PATCH v2 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
  2021-12-20 11:08   ` [PATCH v2 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
@ 2021-12-21 12:30   ` Sean Morrissey
  2021-12-21 12:30     ` [PATCH v3 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
                       ` (2 more replies)
  2 siblings, 3 replies; 46+ messages in thread
From: Sean Morrissey @ 2021-12-21 12:30 UTC (permalink / raw)
  Cc: dev, Sean Morrissey

This patchset introduces config file support for l3fwd
and its lookup methods LPM, FIB, and EM, similar to
that of l3fwd-acl. This allows for route rules to be
defined in configuration files and edited there instead
of in each of the lookup methods hardcoded route tables.

Sean Morrissey (2):
  examples/l3fwd: add config file support for LPM/FIB
  examples/l3fwd: add config file support for EM

 doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
 examples/l3fwd/em_default_v4.cfg        |  17 +
 examples/l3fwd/em_default_v6.cfg        |  17 +
 examples/l3fwd/l3fwd.h                  |  35 ++
 examples/l3fwd/l3fwd_em.c               | 479 ++++++++++++++----------
 examples/l3fwd/l3fwd_fib.c              |  52 +--
 examples/l3fwd/l3fwd_lpm.c              | 281 +++++++++++++-
 examples/l3fwd/l3fwd_route.h            |  49 ++-
 examples/l3fwd/lpm_default_v4.cfg       |  17 +
 examples/l3fwd/lpm_default_v6.cfg       |  17 +
 examples/l3fwd/main.c                   |  99 ++---
 11 files changed, 847 insertions(+), 305 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg

-- 
2.25.1


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

* [PATCH v3 1/2] examples/l3fwd: add config file support for LPM/FIB
  2021-12-21 12:30   ` [PATCH v3 0/2] Add config file support for l3fwd Sean Morrissey
@ 2021-12-21 12:30     ` Sean Morrissey
  2021-12-21 12:30     ` [PATCH v3 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
  2022-01-26 12:44     ` [PATCH v4 0/2] Add config file support for l3fwd Sean Morrissey
  2 siblings, 0 replies; 46+ messages in thread
From: Sean Morrissey @ 2021-12-21 12:30 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Ravi Kerur, Konstantin Ananyev

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for LPM and FIB,
with format similar to l3fwd-acl one.

With the removal of the hardcoded route tables for IPv4
and IPv6, these routes have been moved to a separate
default config file for use with LPM and FIB.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Signed-off-by: Ravi Kerur <rkerur@gmail.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 examples/l3fwd/l3fwd.h            |  35 ++++
 examples/l3fwd/l3fwd_em.c         |   7 +
 examples/l3fwd/l3fwd_fib.c        |  52 +++---
 examples/l3fwd/l3fwd_lpm.c        | 281 +++++++++++++++++++++++++++---
 examples/l3fwd/l3fwd_route.h      |  17 +-
 examples/l3fwd/lpm_default_v4.cfg |  17 ++
 examples/l3fwd/lpm_default_v6.cfg |  17 ++
 examples/l3fwd/main.c             |  99 ++++++-----
 8 files changed, 438 insertions(+), 87 deletions(-)
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg

diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 38ca19133c..d8b1f971e1 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -58,6 +58,30 @@
 #endif
 #define HASH_ENTRY_NUMBER_DEFAULT	16
 
+/*Log file related character defs. */
+#define COMMENT_LEAD_CHAR	('#')
+#define ROUTE_LEAD_CHAR		('R')
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+struct parm_cfg {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+};
+
 struct mbuf_table {
 	uint16_t len;
 	struct rte_mbuf *m_table[MAX_PKT_BURST];
@@ -96,6 +120,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern struct parm_cfg parm_config;
+
 /* Send burst of packets on an output interface */
 static inline int
 send_burst(struct lcore_conf *qconf, uint16_t n, uint16_t port)
@@ -183,6 +209,12 @@ int
 init_mem(uint16_t portid, unsigned int nb_mbuf);
 
 /* Function pointers for LPM, EM or FIB functionality. */
+void
+read_config_files_lpm(void);
+
+void
+read_config_files_em(void);
+
 void
 setup_lpm(const int socketid);
 
@@ -286,4 +318,7 @@ fib_get_ipv4_l3fwd_lookup_struct(const int socketid);
 void *
 fib_get_ipv6_l3fwd_lookup_struct(const int socketid);
 
+int
+is_bypass_line(const char *buff);
+
 #endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 5cc4a4d979..4953cdae4e 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -972,6 +972,13 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+/* Load rules from the input file */
+void
+read_config_files_em(void)
+{
+	/* Empty till config file support added to EM */
+}
+
 /* Initialize exact match (hash) parameters. 8< */
 void
 setup_hash(const int socketid)
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 2110459cc3..003721c908 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -583,7 +583,7 @@ setup_fib(const int socketid)
 	struct rte_eth_dev_info dev_info;
 	struct rte_fib6_conf config;
 	struct rte_fib_conf config_ipv4;
-	unsigned int i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -603,37 +603,39 @@ setup_fib(const int socketid)
 			"Unable to create the l3fwd FIB table on socket %d\n",
 			socketid);
 
+
 	/* Populate the fib ipv4 table. */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* Skip unused ports. */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_fib_add(ipv4_l3fwd_fib_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		if (inet_ntop(AF_INET, &in, abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv4_l3fwd_route_array[i].depth,
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].depth,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv4 route added to port %d [%s]\n",
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		}
 	}
@@ -650,44 +652,50 @@ setup_fib(const int socketid)
 	config.trie.num_tbl8 = (1 << 15);
 	ipv6_l3fwd_fib_lookup_struct[socketid] = rte_fib6_create(s, socketid,
 			&config);
-	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL) {
+		free(route_base_v4);
 		rte_exit(EXIT_FAILURE,
 				"Unable to create the l3fwd FIB table on socket %d\n",
 				socketid);
+	}
 
 	/* Populate the fib IPv6 table. */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* Skip unused ports. */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv6_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_fib6_add(ipv6_l3fwd_fib_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
+			free(route_base_v6);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		if (inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip,
+		if (inet_ntop(AF_INET6, route_base_v6[i].ip_8,
 				abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv6_l3fwd_route_array[i].depth,
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].depth,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv6 route added to port %d [%s]\n",
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		}
 	}
+	free(route_base_v4);
+	free(route_base_v6);
 }
 
 /* Return ipv4 fib lookup struct. */
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index a5b476ced3..4157f16c07 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -39,6 +39,16 @@
 
 static struct rte_lpm *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 static struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
+struct lpm_route_rule *route_base_v4;
+struct lpm_route_rule *route_base_v6;
+int route_num_v4;
+int route_num_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
 
 /* Performing LPM-based lookups. 8< */
 static inline uint16_t
@@ -139,6 +149,199 @@ lpm_get_dst_port_with_ipv4(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
 #include "l3fwd_lpm.h"
 #endif
 
+static int
+parse_ipv6_addr_mask(char *token, uint32_t *ipv6, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET6, sa, ipv6) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 128, 0);
+	return 0;
+}
+
+static int
+parse_ipv4_addr_mask(char *token, uint32_t *ipv4, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET, sa, ipv4) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 32, 0);
+	*ipv4 = ntohl(*ipv4);
+	return 0;
+}
+
+static int
+lpm_parse_v6_net(char *in, uint32_t *v, uint8_t *mask_len)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = parse_ipv6_addr_mask(in, v, mask_len);
+
+	return rc;
+}
+
+static int
+lpm_parse_v6_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], v->ip_32, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_parse_v4_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = parse_ipv4_addr_mask(in[CB_FLD_DST_ADDR], &v->ip, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_add_rules(const char *rule_path,
+		struct lpm_route_rule **proute_base,
+		int (*parser)(char *, struct lpm_route_rule *))
+{
+	struct lpm_route_rule *route_rules;
+	struct lpm_route_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
+static void
+free_lpm_routes(void)
+{
+	free(route_base_v4);
+	free(route_base_v6);
+	route_base_v4 = NULL;
+	route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
+}
+
 /* main processing loop */
 int
 lpm_main_loop(__rte_unused void *dummy)
@@ -153,6 +356,9 @@ lpm_main_loop(__rte_unused void *dummy)
 	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) /
 		US_PER_S * BURST_TX_DRAIN_US;
 
+	/* Config read and setup, free structs */
+	free_lpm_routes();
+
 	lcore_id = rte_lcore_id();
 	qconf = &lcore_conf[lcore_id];
 
@@ -548,13 +754,44 @@ lpm_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+/* Load rules from the input file */
+void
+read_config_files_lpm(void)
+{
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL) {
+		route_num_v4 = lpm_add_rules(parm_config.rule_ipv4_name,
+					&route_base_v4, &lpm_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			free_lpm_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "IPv4 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
+	}
+
+	/* ipv6 check */
+	if (parm_config.rule_ipv6_name != NULL) {
+		route_num_v6 = lpm_add_rules(parm_config.rule_ipv6_name,
+					&route_base_v6, &lpm_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			free_lpm_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "IPv6 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
+	}
+}
+
 void
 setup_lpm(const int socketid)
 {
 	struct rte_eth_dev_info dev_info;
 	struct rte_lpm6_config config;
 	struct rte_lpm_config config_ipv4;
-	unsigned i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -572,32 +809,33 @@ setup_lpm(const int socketid)
 			socketid);
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* skip unused ports */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			free_lpm_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
 		       inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
-		       ipv4_l3fwd_route_array[i].depth,
-		       ipv4_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v4[i].depth,
+		       route_base_v4[i].if_out, dev_info.device->name);
 	}
 
 	/* create the LPM6 table */
@@ -608,37 +846,40 @@ setup_lpm(const int socketid)
 	config.flags = 0;
 	ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid,
 				&config);
-	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL) {
+		free_lpm_routes();
 		rte_exit(EXIT_FAILURE,
 			"Unable to create the l3fwd LPM table on socket %d\n",
 			socketid);
+	}
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			free_lpm_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
-		       inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip, abuf,
+		       inet_ntop(AF_INET6, route_base_v6[i].ip_8, abuf,
 				 sizeof(abuf)),
-		       ipv6_l3fwd_route_array[i].depth,
-		       ipv6_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v6[i].depth,
+		       route_base_v6[i].if_out, dev_info.device->name);
 	}
 }
 
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index c7eba06c4d..7ddd4d9c2d 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -14,6 +14,19 @@ struct ipv6_l3fwd_route {
 	uint8_t if_out;
 };
 
-extern const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[16];
+struct lpm_route_rule {
+	union {
+		uint32_t ip;
+		union {
+			uint32_t ip_32[IPV6_ADDR_U32];
+			uint8_t ip_8[IPV6_ADDR_LEN];
+		};
+	};
+	uint8_t depth;
+	uint8_t if_out;
+};
 
-extern const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[16];
+extern struct lpm_route_rule *route_base_v4;
+extern struct lpm_route_rule *route_base_v6;
+extern int route_num_v4;
+extern int route_num_v6;
diff --git a/examples/l3fwd/lpm_default_v4.cfg b/examples/l3fwd/lpm_default_v4.cfg
new file mode 100644
index 0000000000..4db5597ed8
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD LPM
+R198.18.0.0/24 0
+R198.18.1.0/24 1
+R198.18.2.0/24 2
+R198.18.3.0/24 3
+R198.18.4.0/24 4
+R198.18.5.0/24 5
+R198.18.6.0/24 6
+R198.18.7.0/24 7
+R198.18.8.0/24 8
+R198.18.9.0/24 9
+R198.18.10.0/24 10
+R198.18.11.0/24 11
+R198.18.12.0/24 12
+R198.18.13.0/24 13
+R198.18.14.0/24 14
+R198.18.15.0/24 15
diff --git a/examples/l3fwd/lpm_default_v6.cfg b/examples/l3fwd/lpm_default_v6.cfg
new file mode 100644
index 0000000000..c50233e0ba
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD LPM
+R2001:0200:0000:0000:0000:0000:0000:0000/64 0
+R2001:0200:0000:0001:0000:0000:0000:0000/64 1
+R2001:0200:0000:0002:0000:0000:0000:0000/64 2
+R2001:0200:0000:0003:0000:0000:0000:0000/64 3
+R2001:0200:0000:0004:0000:0000:0000:0000/64 4
+R2001:0200:0000:0005:0000:0000:0000:0000/64 5
+R2001:0200:0000:0006:0000:0000:0000:0000/64 6
+R2001:0200:0000:0007:0000:0000:0000:0000/64 7
+R2001:0200:0000:0008:0000:0000:0000:0000/64 8
+R2001:0200:0000:0009:0000:0000:0000:0000/64 9
+R2001:0200:0000:000A:0000:0000:0000:0000/64 10
+R2001:0200:0000:000B:0000:0000:0000:0000/64 11
+R2001:0200:0000:000C:0000:0000:0000:0000/64 12
+R2001:0200:0000:000D:0000:0000:0000:0000/64 13
+R2001:0200:0000:000E:0000:0000:0000:0000/64 14
+R2001:0200:0000:000F:0000:0000:0000:0000/64 15
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index eb68ffc5aa..927164561e 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -94,6 +94,8 @@ uint32_t hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 
 struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+struct parm_cfg parm_config;
+
 struct lcore_params {
 	uint16_t port_id;
 	uint8_t queue_id;
@@ -141,6 +143,7 @@ static struct rte_mempool *vector_pool[RTE_MAX_ETHPORTS];
 static uint8_t lkp_per_socket[NB_SOCKETS];
 
 struct l3fwd_lkp_mode {
+	void  (*read_config_files)(void);
 	void  (*setup)(int);
 	int   (*check_ptype)(int);
 	rte_rx_callback_fn cb_parse_ptype;
@@ -152,6 +155,7 @@ struct l3fwd_lkp_mode {
 static struct l3fwd_lkp_mode l3fwd_lkp;
 
 static struct l3fwd_lkp_mode l3fwd_em_lkp = {
+	.read_config_files		= read_config_files_em,
 	.setup                  = setup_hash,
 	.check_ptype		= em_check_ptype,
 	.cb_parse_ptype		= em_cb_parse_ptype,
@@ -161,6 +165,7 @@ static struct l3fwd_lkp_mode l3fwd_em_lkp = {
 };
 
 static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_lpm,
 	.check_ptype		= lpm_check_ptype,
 	.cb_parse_ptype		= lpm_cb_parse_ptype,
@@ -170,6 +175,7 @@ static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
 };
 
 static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_fib,
 	.check_ptype            = lpm_check_ptype,
 	.cb_parse_ptype         = lpm_cb_parse_ptype,
@@ -179,50 +185,19 @@ static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
 };
 
 /*
- * 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
- * 198.18.{0-15}.0/24 = Port {0-15}
+ * API's called during initialization to setup ACL/EM/LPM rules.
  */
-const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[] = {
-	{RTE_IPV4(198, 18, 0, 0), 24, 0},
-	{RTE_IPV4(198, 18, 1, 0), 24, 1},
-	{RTE_IPV4(198, 18, 2, 0), 24, 2},
-	{RTE_IPV4(198, 18, 3, 0), 24, 3},
-	{RTE_IPV4(198, 18, 4, 0), 24, 4},
-	{RTE_IPV4(198, 18, 5, 0), 24, 5},
-	{RTE_IPV4(198, 18, 6, 0), 24, 6},
-	{RTE_IPV4(198, 18, 7, 0), 24, 7},
-	{RTE_IPV4(198, 18, 8, 0), 24, 8},
-	{RTE_IPV4(198, 18, 9, 0), 24, 9},
-	{RTE_IPV4(198, 18, 10, 0), 24, 10},
-	{RTE_IPV4(198, 18, 11, 0), 24, 11},
-	{RTE_IPV4(198, 18, 12, 0), 24, 12},
-	{RTE_IPV4(198, 18, 13, 0), 24, 13},
-	{RTE_IPV4(198, 18, 14, 0), 24, 14},
-	{RTE_IPV4(198, 18, 15, 0), 24, 15},
-};
+static void
+l3fwd_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
 
-/*
- * 2001:200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180).
- * 2001:200:0:{0-f}::/64 = Port {0-15}
- */
-const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[] = {
-	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 0},
-	{{32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 1},
-	{{32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 2},
-	{{32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 3},
-	{{32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 4},
-	{{32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 5},
-	{{32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 6},
-	{{32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 7},
-	{{32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 8},
-	{{32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 9},
-	{{32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 10},
-	{{32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 11},
-	{{32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 12},
-	{{32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 13},
-	{{32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 14},
-	{{32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 15},
-};
+static void
+l3fwd_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
 
 /*
  * Setup lookup methods for forwarding.
@@ -339,6 +314,8 @@ print_usage(const char *prgname)
 {
 	fprintf(stderr, "%s [EAL options] --"
 		" -p PORTMASK"
+		"  --rule_ipv4=FILE"
+		"  --rule_ipv6=FILE"
 		" [-P]"
 		" [--lookup]"
 		" --config (port,queue,lcore)[,(port,queue,lcore)]"
@@ -381,7 +358,10 @@ print_usage(const char *prgname)
 		"  --event-vector-size: Max vector size if event vectorization is enabled.\n"
 		"  --event-vector-tmo: Max timeout to form vector in nanoseconds if event vectorization is enabled\n"
 		"  -E : Enable exact match, legacy flag please use --lookup=em instead\n"
-		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n\n",
+		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n"
+		"  --rule_ipv4=FILE: Specify the ipv4 rules entries file.\n"
+		"                    Each rule occupies one line.\n"
+		"  --rule_ipv6=FILE: Specify the ipv6 rules entries file.\n\n",
 		prgname);
 }
 
@@ -596,6 +576,8 @@ static const char short_options[] =
 #define CMD_LINE_OPT_ENABLE_VECTOR "event-vector"
 #define CMD_LINE_OPT_VECTOR_SIZE "event-vector-size"
 #define CMD_LINE_OPT_VECTOR_TMO_NS "event-vector-tmo"
+#define CMD_LINE_OPT_RULE_IPV4 "rule_ipv4"
+#define CMD_LINE_OPT_RULE_IPV6 "rule_ipv6"
 
 enum {
 	/* long options mapped to a short option */
@@ -610,6 +592,8 @@ enum {
 	CMD_LINE_OPT_MAX_PKT_LEN_NUM,
 	CMD_LINE_OPT_HASH_ENTRY_NUM_NUM,
 	CMD_LINE_OPT_PARSE_PTYPE_NUM,
+	CMD_LINE_OPT_RULE_IPV4_NUM,
+	CMD_LINE_OPT_RULE_IPV6_NUM,
 	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
 	CMD_LINE_OPT_MODE_NUM,
 	CMD_LINE_OPT_EVENTQ_SYNC_NUM,
@@ -637,6 +621,8 @@ static const struct option lgopts[] = {
 	{CMD_LINE_OPT_ENABLE_VECTOR, 0, 0, CMD_LINE_OPT_ENABLE_VECTOR_NUM},
 	{CMD_LINE_OPT_VECTOR_SIZE, 1, 0, CMD_LINE_OPT_VECTOR_SIZE_NUM},
 	{CMD_LINE_OPT_VECTOR_TMO_NS, 1, 0, CMD_LINE_OPT_VECTOR_TMO_NS_NUM},
+	{CMD_LINE_OPT_RULE_IPV4,   1, 0, CMD_LINE_OPT_RULE_IPV4_NUM},
+	{CMD_LINE_OPT_RULE_IPV6,   1, 0, CMD_LINE_OPT_RULE_IPV6_NUM},
 	{NULL, 0, 0, 0}
 };
 
@@ -791,6 +777,12 @@ parse_args(int argc, char **argv)
 		case CMD_LINE_OPT_VECTOR_TMO_NS_NUM:
 			evt_rsrc->vector_tmo_ns = strtoull(optarg, NULL, 10);
 			break;
+		case CMD_LINE_OPT_RULE_IPV4_NUM:
+			l3fwd_set_rule_ipv4_name(optarg);
+			break;
+		case CMD_LINE_OPT_RULE_IPV6_NUM:
+			l3fwd_set_rule_ipv6_name(optarg);
+			break;
 		default:
 			print_usage(prgname);
 			return -1;
@@ -1358,6 +1350,24 @@ l3fwd_event_service_setup(void)
 	}
 }
 
+/* Bypass comment and empty lines */
+int
+is_bypass_line(const char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
 int
 main(int argc, char **argv)
 {
@@ -1395,6 +1405,9 @@ main(int argc, char **argv)
 	/* Setup function pointers for lookup method. */
 	setup_l3fwd_lookup_tables();
 
+	/* Add the config file rules */
+	l3fwd_lkp.read_config_files();
+
 	evt_rsrc->per_port_pool = per_port_pool;
 	evt_rsrc->pkt_pool = pktmbuf_pool;
 	evt_rsrc->vec_pool = vector_pool;
-- 
2.25.1


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

* [PATCH v3 2/2] examples/l3fwd: add config file support for EM
  2021-12-21 12:30   ` [PATCH v3 0/2] Add config file support for l3fwd Sean Morrissey
  2021-12-21 12:30     ` [PATCH v3 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
@ 2021-12-21 12:30     ` Sean Morrissey
  2022-01-10 14:00       ` Ananyev, Konstantin
  2022-01-26 12:44     ` [PATCH v4 0/2] Add config file support for l3fwd Sean Morrissey
  2 siblings, 1 reply; 46+ messages in thread
From: Sean Morrissey @ 2021-12-21 12:30 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Ravi Kerur

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for EM with a format
similar to l3fwd-acl one.

With the removal of the hardcoded route tables for IPv4
and IPv6 from 'l3fwd_em', these routes have been moved
to a separate default config file for use with EM.

Related l3fwd docs have been updated to relfect these
changes.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Signed-off-by: Ravi Kerur <rkerur@gmail.com>
---
 doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
 examples/l3fwd/em_default_v4.cfg        |  17 +
 examples/l3fwd/em_default_v6.cfg        |  17 +
 examples/l3fwd/l3fwd_em.c               | 474 ++++++++++++++----------
 examples/l3fwd/l3fwd_route.h            |  38 +-
 5 files changed, 413 insertions(+), 222 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg

diff --git a/doc/guides/sample_app_ug/l3_forward.rst b/doc/guides/sample_app_ug/l3_forward.rst
index 6d7d7c5cc1..01d86db95d 100644
--- a/doc/guides/sample_app_ug/l3_forward.rst
+++ b/doc/guides/sample_app_ug/l3_forward.rst
@@ -47,6 +47,7 @@ and loaded into the LPM or FIB object at initialization time.
 In the sample application, hash-based and FIB-based forwarding supports
 both IPv4 and IPv6.
 LPM-based forwarding supports IPv4 only.
+During the initialization phase route rules for IPv4 and IPv6 are read from rule files.
 
 Compiling the Application
 -------------------------
@@ -61,6 +62,8 @@ Running the Application
 The application has a number of command line options::
 
     ./dpdk-l3fwd [EAL options] -- -p PORTMASK
+                             --rule_ipv4=FILE
+                             --rule_ipv6=FILE
                              [-P]
                              [--lookup LOOKUP_METHOD]
                              --config(port,queue,lcore)[,(port,queue,lcore)]
@@ -82,6 +85,11 @@ Where,
 
 * ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
 
+* ``--rule_ipv4=FILE:`` specify the ipv4 rules entries file.
+  Each rule occupies one line.
+
+* ``--rule_ipv6=FILE:`` specify the ipv6 rules entries file.
+
 * ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
   Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
 
@@ -135,7 +143,7 @@ To enable L3 forwarding between two ports, assuming that both ports are in the s
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
+    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)" --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -157,19 +165,23 @@ In this command:
 |          |           |           |                                     |
 +----------+-----------+-----------+-------------------------------------+
 
+*   The -rule_ipv4 option specifies the reading of IPv4 rules sets from the rule_ipv4.cfg file
+
+*   The -rule_ipv6 option specifies the reading of IPv6 rules sets from the rule_ipv6.cfg file.
+
 To use eventdev mode with sync method **ordered** on above mentioned environment,
 Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 or
 
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> \
-		-- -p 0x03 --mode=eventdev --eventq-sched=ordered
+		-- -p 0x03 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -192,7 +204,7 @@ scheduler. Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In case of eventdev mode, *--config* option is not used for ethernet port
 configuration. Instead each ethernet port will be configured with mentioned
@@ -216,6 +228,49 @@ The following sections provide some explanation of the sample application code.
 the initialization and run-time paths are very similar to those of the :doc:`l2_forward_real_virtual` and :doc:`l2_forward_event`.
 The following sections describe aspects that are specific to the L3 Forwarding sample application.
 
+Parse Rules from File
+~~~~~~~~~~~~~~~~~~~~~
+
+The application parses the rules from the file and adds them to the appropriate route table by calling the appropriate function.
+It ignores empty and comment lines, and parses and validates the rules it reads.
+If errors are detected, the application exits with messages to identify the errors encountered.
+
+The format of the route rules differs based on which lookup method is being used.
+Therefore, the code only decreases the priority number with each rule it parses.
+Route rules are mandatory.
+To read data from the specified file successfully, the application assumes the following:
+
+*   Each rule occupies a single line.
+
+*   Only the following four rule line types are valid in this application:
+
+*   Route rule line, which starts with a leading character 'R'
+
+*   Comment line, which starts with a leading character '#'
+
+*   Empty line, which consists of a space, form-feed ('\f'), newline ('\n'),
+    carriage return ('\r'), horizontal tab ('\t'), or vertical tab ('\v').
+
+Other lines types are considered invalid.
+
+*   Rules are organized in descending order of priority,
+    which means rules at the head of the file always have a higher priority than those further down in the file.
+
+*   A typical IPv4 LPM/FIB rule line should have a format as shown below:
+
+R<destination_ip>/<ip_mask_length><output_port_number>
+
+*   A typical IPv4 EM rule line should have a format as shown below:
+
+R<destination_ip><source_ip><destination_port><source_port><protocol><output_port_number>
+
+IPv4 addresses are specified in CIDR format as specified in RFC 4632.
+For LPM/FIB they consist of the dot notation for the address and a prefix length separated by '/'.
+For example, 192.168.0.34/32, where the address is 192.168.0.34 and the prefix length is 32.
+For EM they consist of just the dot notation for the address and no prefix length.
+For example, 192.168.0.34, where the Address is 192.168.0.34.
+EM also includes ports which are specified as a single number which represents a single port.
+
 Hash Initialization
 ~~~~~~~~~~~~~~~~~~~
 
@@ -227,8 +282,6 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
 
     The Hash initialization will setup both ipv4 and ipv6 hash table,
     and populate the either table depending on the value of variable ipv6.
-    To support the hash performance test with up to 8M single direction flows/16M bi-direction flows,
-    populate_ipv4_many_flow_into_table() function will populate the hash table with specified hash table entry number(default 4M).
 
 .. note::
 
@@ -246,22 +299,14 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
         {
             // ...
 
-            if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_many_flow_into_table(ipv4_l3fwd_lookup_struct[socketid], hash_entry_number);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_many_flow_into_table( ipv6_l3fwd_lookup_struct[socketid], hash_entry_number);
-                }
-            } else
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_few_flow_into_table(ipv4_l3fwd_lookup_struct[socketid]);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_few_flow_into_table(ipv6_l3fwd_lookup_struct[socketid]);
-                }
+            if (ipv6 == 0) {
+                /* populate the ipv4 hash */
+                populate_ipv4_flow_into_table(
+                    ipv4_l3fwd_em_lookup_struct[socketid]);
+            } else {
+                /* populate the ipv6 hash */
+                populate_ipv6_flow_into_table(
+                    ipv6_l3fwd_em_lookup_struct[socketid]);
             }
         }
     #endif
diff --git a/examples/l3fwd/em_default_v4.cfg b/examples/l3fwd/em_default_v4.cfg
new file mode 100644
index 0000000000..b70bdb6601
--- /dev/null
+++ b/examples/l3fwd/em_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD EM
+R198.18.0.0 198.18.0.1 9 9 0x11 0
+R198.18.1.0 198.18.1.1 9 9 0x11 1
+R198.18.2.0 198.18.2.1 9 9 0x11 2
+R198.18.3.0 198.18.3.1 9 9 0x11 3
+R198.18.4.0 198.18.4.1 9 9 0x11 4
+R198.18.5.0 198.18.5.1 9 9 0x11 5
+R198.18.6.0 198.18.6.1 9 9 0x11 6
+R198.18.7.0 198.18.7.1 9 9 0x11 7
+R198.18.8.0 198.18.8.1 9 9 0x11 8
+R198.18.9.0 198.18.9.1 9 9 0x11 9
+R198.18.10.0 198.18.10.1 9 9 0x11 10
+R198.18.11.0 198.18.11.1 9 9 0x11 11
+R198.18.12.0 198.18.12.1 9 9 0x11 12
+R198.18.13.0 198.18.13.1 9 9 0x11 13
+R198.18.14.0 198.18.14.1 9 9 0x11 14
+R198.18.15.0 198.18.15.1 9 9 0x11 15
diff --git a/examples/l3fwd/em_default_v6.cfg b/examples/l3fwd/em_default_v6.cfg
new file mode 100644
index 0000000000..0b94f8d8d2
--- /dev/null
+++ b/examples/l3fwd/em_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD EM
+R2001:0200:0000:0000:0000:0000:0000:0000 2001:0200:0000:0000:0000:0000:0000:0001 9 9 0x11 0
+R2001:0200:0000:0001:0000:0000:0000:0000 2001:0200:0000:0001:0000:0000:0000:0001 9 9 0x11 1
+R2001:0200:0000:0002:0000:0000:0000:0000 2001:0200:0000:0002:0000:0000:0000:0001 9 9 0x11 2
+R2001:0200:0000:0003:0000:0000:0000:0000 2001:0200:0000:0003:0000:0000:0000:0001 9 9 0x11 3
+R2001:0200:0000:0004:0000:0000:0000:0000 2001:0200:0000:0004:0000:0000:0000:0001 9 9 0x11 4
+R2001:0200:0000:0005:0000:0000:0000:0000 2001:0200:0000:0005:0000:0000:0000:0001 9 9 0x11 5
+R2001:0200:0000:0006:0000:0000:0000:0000 2001:0200:0000:0006:0000:0000:0000:0001 9 9 0x11 6
+R2001:0200:0000:0007:0000:0000:0000:0000 2001:0200:0000:0007:0000:0000:0000:0001 9 9 0x11 7
+R2001:0200:0000:0008:0000:0000:0000:0000 2001:0200:0000:0008:0000:0000:0000:0001 9 9 0x11 8
+R2001:0200:0000:0009:0000:0000:0000:0000 2001:0200:0000:0009:0000:0000:0000:0001 9 9 0x11 9
+R2001:0200:0000:000A:0000:0000:0000:0000 2001:0200:0000:000A:0000:0000:0000:0001 9 9 0x11 10
+R2001:0200:0000:000B:0000:0000:0000:0000 2001:0200:0000:000B:0000:0000:0000:0001 9 9 0x11 11
+R2001:0200:0000:000C:0000:0000:0000:0000 2001:0200:0000:000C:0000:0000:0000:0001 9 9 0x11 12
+R2001:0200:0000:000D:0000:0000:0000:0000 2001:0200:0000:000D:0000:0000:0000:0001 9 9 0x11 13
+R2001:0200:0000:000E:0000:0000:0000:0000 2001:0200:0000:000E:0000:0000:0000:0001 9 9 0x11 14
+R2001:0200:0000:000F:0000:0000:0000:0000 2001:0200:0000:000F:0000:0000:0000:0001 9 9 0x11 15
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 4953cdae4e..f99007f536 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -27,6 +27,7 @@
 
 #include "l3fwd.h"
 #include "l3fwd_event.h"
+#include "l3fwd_route.h"
 
 #if defined(RTE_ARCH_X86) || defined(__ARM_FEATURE_CRC32)
 #define EM_HASH_CRC 1
@@ -42,13 +43,18 @@
 
 #define IPV6_ADDR_LEN 16
 
-struct ipv4_5tuple {
-	uint32_t ip_dst;
-	uint32_t ip_src;
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
+static struct em_rule *em_route_base_v4;
+static struct em_rule *em_route_base_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_SRC_ADDR,
+	CB_FLD_DST_PORT,
+	CB_FLD_SRC_PORT,
+	CB_FLD_PROTO,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
 
 union ipv4_5tuple_host {
 	struct {
@@ -65,14 +71,6 @@ union ipv4_5tuple_host {
 
 #define XMM_NUM_IN_IPV6_5TUPLE 3
 
-struct ipv6_5tuple {
-	uint8_t  ip_dst[IPV6_ADDR_LEN];
-	uint8_t  ip_src[IPV6_ADDR_LEN];
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
-
 union ipv6_5tuple_host {
 	struct {
 		uint16_t pad0;
@@ -87,8 +85,6 @@ union ipv6_5tuple_host {
 	xmm_t xmm[XMM_NUM_IN_IPV6_5TUPLE];
 };
 
-
-
 struct ipv4_l3fwd_em_route {
 	struct ipv4_5tuple key;
 	uint8_t if_out;
@@ -99,66 +95,6 @@ struct ipv6_l3fwd_em_route {
 	uint8_t if_out;
 };
 
-/* 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
- * Use RFC863 Discard Protocol.
- */
-static const struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[] = {
-	{{RTE_IPV4(198, 18, 0, 0), RTE_IPV4(198, 18, 0, 1),  9, 9, IPPROTO_UDP}, 0},
-	{{RTE_IPV4(198, 18, 1, 0), RTE_IPV4(198, 18, 1, 1),  9, 9, IPPROTO_UDP}, 1},
-	{{RTE_IPV4(198, 18, 2, 0), RTE_IPV4(198, 18, 2, 1),  9, 9, IPPROTO_UDP}, 2},
-	{{RTE_IPV4(198, 18, 3, 0), RTE_IPV4(198, 18, 3, 1),  9, 9, IPPROTO_UDP}, 3},
-	{{RTE_IPV4(198, 18, 4, 0), RTE_IPV4(198, 18, 4, 1),  9, 9, IPPROTO_UDP}, 4},
-	{{RTE_IPV4(198, 18, 5, 0), RTE_IPV4(198, 18, 5, 1),  9, 9, IPPROTO_UDP}, 5},
-	{{RTE_IPV4(198, 18, 6, 0), RTE_IPV4(198, 18, 6, 1),  9, 9, IPPROTO_UDP}, 6},
-	{{RTE_IPV4(198, 18, 7, 0), RTE_IPV4(198, 18, 7, 1),  9, 9, IPPROTO_UDP}, 7},
-	{{RTE_IPV4(198, 18, 8, 0), RTE_IPV4(198, 18, 8, 1),  9, 9, IPPROTO_UDP}, 8},
-	{{RTE_IPV4(198, 18, 9, 0), RTE_IPV4(198, 18, 9, 1),  9, 9, IPPROTO_UDP}, 9},
-	{{RTE_IPV4(198, 18, 10, 0), RTE_IPV4(198, 18, 10, 1),  9, 9, IPPROTO_UDP}, 10},
-	{{RTE_IPV4(198, 18, 11, 0), RTE_IPV4(198, 18, 11, 1),  9, 9, IPPROTO_UDP}, 11},
-	{{RTE_IPV4(198, 18, 12, 0), RTE_IPV4(198, 18, 12, 1),  9, 9, IPPROTO_UDP}, 12},
-	{{RTE_IPV4(198, 18, 13, 0), RTE_IPV4(198, 18, 13, 1),  9, 9, IPPROTO_UDP}, 13},
-	{{RTE_IPV4(198, 18, 14, 0), RTE_IPV4(198, 18, 14, 1),  9, 9, IPPROTO_UDP}, 14},
-	{{RTE_IPV4(198, 18, 15, 0), RTE_IPV4(198, 18, 15, 1),  9, 9, IPPROTO_UDP}, 15},
-};
-
-/* 2001:0200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180).
- * Use RFC863 Discard Protocol.
- */
-static const struct ipv6_l3fwd_em_route ipv6_l3fwd_em_route_array[] = {
-	{{{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 0},
-	{{{32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 1},
-	{{{32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 2},
-	{{{32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 3},
-	{{{32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 4},
-	{{{32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 5},
-	{{{32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 6},
-	{{{32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 7},
-	{{{32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 8},
-	{{{32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 9},
-	{{{32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 10},
-	{{{32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 11},
-	{{{32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 12},
-	{{{32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 13},
-	{{{32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 14},
-	{{{32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 15},
-};
-
 struct rte_hash *ipv4_l3fwd_em_lookup_struct[NB_SOCKETS];
 struct rte_hash *ipv6_l3fwd_em_lookup_struct[NB_SOCKETS];
 
@@ -235,10 +171,6 @@ ipv6_hash_crc(const void *data, __rte_unused uint32_t data_len,
 	return init_val;
 }
 
-#define IPV4_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv4_l3fwd_em_route_array)
-
-#define IPV6_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv6_l3fwd_em_route_array)
-
 static uint8_t ipv4_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 static uint8_t ipv6_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 
@@ -346,6 +278,174 @@ em_get_ipv6_dst_port(void *ipv6_hdr, uint16_t portid, void *lookup_struct)
 #include "l3fwd_em.h"
 #endif
 
+static int
+em_parse_v6_net(const char *in, uint8_t *v)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = inet_pton(AF_INET6, in, v);
+	if (rc != 1)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+em_parse_v6_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], v->v6_key.ip_dst);
+	if (rc != 0)
+		return rc;
+	rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], v->v6_key.ip_src);
+	if (rc != 0)
+		return rc;
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_parse_v4_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = inet_pton(AF_INET, in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
+	v->v4_key.ip_dst = ntohl(v->v4_key.ip_dst);
+	if (rc != 1)
+		return rc;
+
+	rc = inet_pton(AF_INET, in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
+	v->v4_key.ip_src = ntohl(v->v4_key.ip_src);
+	if (rc != 1)
+		return rc;
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_add_rules(const char *rule_path,
+		struct em_rule **proute_base,
+		int (*parser)(char *, struct em_rule *))
+{
+	struct em_rule *route_rules;
+	struct em_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
 static void
 convert_ipv4_5tuple(struct ipv4_5tuple *key1,
 		union ipv4_5tuple_host *key2)
@@ -382,122 +482,98 @@ convert_ipv6_5tuple(struct ipv6_5tuple *key1,
 #define BIT_8_TO_15 0x0000ff00
 
 static inline void
-populate_ipv4_few_flow_into_table(const struct rte_hash *h)
+populate_ipv4_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
-	for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv4_l3fwd_em_route  entry;
+	for (i = 0; i < route_num_v4; i++) {
+		struct em_rule *entry;
 		union ipv4_5tuple_host newkey;
+		struct in_addr src;
+		struct in_addr dst;
 
-		entry = ipv4_l3fwd_em_route_array[i];
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v4[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v4[i];
+		convert_ipv4_5tuple(&(entry->v4_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv4_l3fwd_out_if[ret] = entry.if_out;
+		ipv4_l3fwd_out_if[ret] = entry->if_out;
+		rte_eth_dev_info_get(em_route_base_v4[i].if_out,
+				     &dev_info);
+
+		src.s_addr = htonl(em_route_base_v4[i].v4_key.ip_src);
+		dst.s_addr = htonl(em_route_base_v4[i].v4_key.ip_dst);
+		printf("EM: Adding route %s, %s, %d, %d, %d (%d) [%s]\n",
+			   inet_ntop(AF_INET, &dst, dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET, &src, srcbuf, sizeof(srcbuf)),
+			   em_route_base_v4[i].v4_key.port_dst,
+			   em_route_base_v4[i].v4_key.port_src,
+			   em_route_base_v4[i].v4_key.proto,
+		       em_route_base_v4[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 " keys\n",
-		(uint64_t)IPV4_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v4);
 }
 
 #define BIT_16_TO_23 0x00ff0000
 static inline void
-populate_ipv6_few_flow_into_table(const struct rte_hash *h)
+populate_ipv6_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
-	for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv6_l3fwd_em_route entry;
+	for (i = 0; i < route_num_v6; i++) {
+		struct em_rule *entry;
 		union ipv6_5tuple_host newkey;
 
-		entry = ipv6_l3fwd_em_route_array[i];
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v6[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v6[i];
+		convert_ipv6_5tuple(&(entry->v6_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv6_l3fwd_out_if[ret] = entry.if_out;
+		ipv6_l3fwd_out_if[ret] = entry->if_out;
+		rte_eth_dev_info_get(em_route_base_v6[i].if_out,
+				     &dev_info);
+
+		printf("EM: Adding route %s, %s, %d, %d, %d (%d) [%s]\n",
+			   inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip32_dst,
+			   dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip32_src,
+			   srcbuf, sizeof(srcbuf)),
+			   em_route_base_v6[i].v6_key.port_dst,
+			   em_route_base_v6[i].v6_key.port_src,
+			   em_route_base_v6[i].v6_key.proto,
+		       em_route_base_v6[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 "keys\n",
-		(uint64_t)IPV6_L3FWD_EM_NUM_ROUTES);
-}
-
-#define NUMBER_PORT_USED 16
-static inline void
-populate_ipv4_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv4_l3fwd_em_route entry;
-		union ipv4_5tuple_host newkey;
-
-		uint8_t a = (uint8_t)((port + 1) % BYTE_VALUE_MAX);
-
-		/* Create the ipv4 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv4_l3fwd_em_route_array[port];
-		entry.key.ip_dst = RTE_IPV4(198, 18, port, a);
-		convert_ipv4_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv4_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
-}
-
-static inline void
-populate_ipv6_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv6_l3fwd_em_route entry;
-		union ipv6_5tuple_host newkey;
-
-		/* Create the ipv6 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv6_l3fwd_em_route_array[port];
-		entry.key.ip_dst[15] = (port + 1) % BYTE_VALUE_MAX;
-		convert_ipv6_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv6_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
+		(uint64_t)route_num_v6);
 }
 
 /* Requirements:
@@ -972,11 +1048,46 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+static void
+free_em_routes(void)
+{
+	free(em_route_base_v4);
+	free(em_route_base_v6);
+	em_route_base_v4 = NULL;
+	em_route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
+}
+
 /* Load rules from the input file */
 void
 read_config_files_em(void)
 {
-	/* Empty till config file support added to EM */
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL) {
+		route_num_v4 = em_add_rules(parm_config.rule_ipv4_name,
+					&em_route_base_v4, &em_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			free_em_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv4 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "EM IPv4 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+	}
+
+	/* ipv6 check */
+	if (parm_config.rule_ipv6_name != NULL) {
+		route_num_v6 = em_add_rules(parm_config.rule_ipv6_name,
+					&em_route_base_v6, &em_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			free_em_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "EM IPv6 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+	}
 }
 
 /* Initialize exact match (hash) parameters. 8< */
@@ -1023,35 +1134,18 @@ setup_hash(const int socketid)
 			"Unable to create the l3fwd hash on socket %d\n",
 			socketid);
 
-	if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-		/* For testing hash matching with a large number of flows we
-		 * generate millions of IP 5-tuples with an incremented dst
-		 * address to initialize the hash table. */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_many_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_many_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		}
+	/*
+	 * Use data from ipv4/ipv6 l3fwd config file
+	 * directly to initialize the hash table.
+	 */
+	if (ipv6 == 0) {
+		/* populate the ipv4 hash */
+		populate_ipv4_flow_into_table(
+			ipv4_l3fwd_em_lookup_struct[socketid]);
 	} else {
-		/*
-		 * Use data in ipv4/ipv6 l3fwd lookup table
-		 * directly to initialize the hash table.
-		 */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_few_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid]);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_few_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid]);
-		}
+		/* populate the ipv6 hash */
+		populate_ipv6_flow_into_table(
+			ipv6_l3fwd_em_lookup_struct[socketid]);
 	}
 }
 /* >8 End of initialization of hash parameters. */
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index 7ddd4d9c2d..ce62aa9b3e 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -2,17 +2,27 @@
  * Copyright(c) 2021 Intel Corporation
  */
 
-struct ipv4_l3fwd_route {
-	uint32_t ip;
-	uint8_t  depth;
-	uint8_t  if_out;
-};
+struct ipv4_5tuple {
+	uint32_t ip_dst;
+	uint32_t ip_src;
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
 
-struct ipv6_l3fwd_route {
-	uint8_t ip[16];
-	uint8_t depth;
-	uint8_t if_out;
-};
+struct ipv6_5tuple {
+	union {
+		uint8_t  ip_dst[IPV6_ADDR_LEN];
+		uint32_t ip32_dst[IPV6_ADDR_U32];
+	};
+	union {
+		uint8_t  ip_src[IPV6_ADDR_LEN];
+		uint32_t ip32_src[IPV6_ADDR_U32];
+	};
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
 
 struct lpm_route_rule {
 	union {
@@ -26,6 +36,14 @@ struct lpm_route_rule {
 	uint8_t if_out;
 };
 
+struct em_rule {
+		union {
+		struct ipv4_5tuple v4_key;
+		struct ipv6_5tuple v6_key;
+	};
+	uint8_t if_out;
+};
+
 extern struct lpm_route_rule *route_base_v4;
 extern struct lpm_route_rule *route_base_v6;
 extern int route_num_v4;
-- 
2.25.1


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

* RE: [PATCH v3 2/2] examples/l3fwd: add config file support for EM
  2021-12-21 12:30     ` [PATCH v3 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
@ 2022-01-10 14:00       ` Ananyev, Konstantin
  0 siblings, 0 replies; 46+ messages in thread
From: Ananyev, Konstantin @ 2022-01-10 14:00 UTC (permalink / raw)
  To: Morrissey, Sean; +Cc: dev, Morrissey, Sean, Ravi Kerur


 
> Add support to define ipv4 and ipv6 forwarding tables
> from reading from a config file for EM with a format
> similar to l3fwd-acl one.
> 
> With the removal of the hardcoded route tables for IPv4
> and IPv6 from 'l3fwd_em', these routes have been moved
> to a separate default config file for use with EM.
> 
> Related l3fwd docs have been updated to relfect these
> changes.
> 
> Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
> Signed-off-by: Ravi Kerur <rkerur@gmail.com>
> ---

Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

> 2.25.1


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

* [PATCH v4 0/2] Add config file support for l3fwd
  2021-12-21 12:30   ` [PATCH v3 0/2] Add config file support for l3fwd Sean Morrissey
  2021-12-21 12:30     ` [PATCH v3 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
  2021-12-21 12:30     ` [PATCH v3 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
@ 2022-01-26 12:44     ` Sean Morrissey
  2022-01-26 12:44       ` [PATCH v4 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
                         ` (2 more replies)
  2 siblings, 3 replies; 46+ messages in thread
From: Sean Morrissey @ 2022-01-26 12:44 UTC (permalink / raw)
  Cc: dev, Sean Morrissey

This patchset introduces config file support for l3fwd
and its lookup methods LPM, FIB, and EM, similar to
that of l3fwd-acl. This allows for route rules to be
defined in configuration files and edited there instead
of in each of the lookup methods hardcoded route tables.

V4:
* Fix nondeterministic bug of segfault on termination of
  sample app.

Sean Morrissey (2):
  examples/l3fwd: add config file support for LPM/FIB
  examples/l3fwd: add config file support for EM

 doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
 examples/l3fwd/em_default_v4.cfg        |  17 +
 examples/l3fwd/em_default_v6.cfg        |  17 +
 examples/l3fwd/l3fwd.h                  |  41 ++
 examples/l3fwd/l3fwd_em.c               | 479 ++++++++++++++----------
 examples/l3fwd/l3fwd_fib.c              |  52 +--
 examples/l3fwd/l3fwd_lpm.c              | 278 +++++++++++++-
 examples/l3fwd/l3fwd_route.h            |  49 ++-
 examples/l3fwd/lpm_default_v4.cfg       |  17 +
 examples/l3fwd/lpm_default_v6.cfg       |  17 +
 examples/l3fwd/main.c                   | 106 +++---
 11 files changed, 857 insertions(+), 305 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg

-- 
2.25.1


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

* [PATCH v4 1/2] examples/l3fwd: add config file support for LPM/FIB
  2022-01-26 12:44     ` [PATCH v4 0/2] Add config file support for l3fwd Sean Morrissey
@ 2022-01-26 12:44       ` Sean Morrissey
  2022-01-26 12:44       ` [PATCH v4 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
  2022-02-04 19:59       ` [PATCH v5 0/2] Add config file support for l3fwd Sean Morrissey
  2 siblings, 0 replies; 46+ messages in thread
From: Sean Morrissey @ 2022-01-26 12:44 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Ravi Kerur, Konstantin Ananyev

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for LPM and FIB,
with format similar to l3fwd-acl one.

With the removal of the hardcoded route tables for IPv4
and IPv6, these routes have been moved to a separate
default config file for use with LPM and FIB.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Signed-off-by: Ravi Kerur <rkerur@gmail.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 examples/l3fwd/l3fwd.h            |  41 +++++
 examples/l3fwd/l3fwd_em.c         |  13 ++
 examples/l3fwd/l3fwd_fib.c        |  52 +++---
 examples/l3fwd/l3fwd_lpm.c        | 278 +++++++++++++++++++++++++++---
 examples/l3fwd/l3fwd_route.h      |  17 +-
 examples/l3fwd/lpm_default_v4.cfg |  17 ++
 examples/l3fwd/lpm_default_v6.cfg |  17 ++
 examples/l3fwd/main.c             | 106 +++++++-----
 8 files changed, 454 insertions(+), 87 deletions(-)
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg

diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 38ca19133c..4e1e0af033 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -58,6 +58,30 @@
 #endif
 #define HASH_ENTRY_NUMBER_DEFAULT	16
 
+/*Log file related character defs. */
+#define COMMENT_LEAD_CHAR	('#')
+#define ROUTE_LEAD_CHAR		('R')
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+struct parm_cfg {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+};
+
 struct mbuf_table {
 	uint16_t len;
 	struct rte_mbuf *m_table[MAX_PKT_BURST];
@@ -96,6 +120,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern struct parm_cfg parm_config;
+
 /* Send burst of packets on an output interface */
 static inline int
 send_burst(struct lcore_conf *qconf, uint16_t n, uint16_t port)
@@ -183,6 +209,12 @@ int
 init_mem(uint16_t portid, unsigned int nb_mbuf);
 
 /* Function pointers for LPM, EM or FIB functionality. */
+void
+read_config_files_lpm(void);
+
+void
+read_config_files_em(void);
+
 void
 setup_lpm(const int socketid);
 
@@ -286,4 +318,13 @@ fib_get_ipv4_l3fwd_lookup_struct(const int socketid);
 void *
 fib_get_ipv6_l3fwd_lookup_struct(const int socketid);
 
+void
+em_free_routes(void);
+
+void
+lpm_free_routes(void);
+
+int
+is_bypass_line(const char *buff);
+
 #endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 5cc4a4d979..9058e75a76 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -972,6 +972,19 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+/* Load rules from the input file */
+void
+read_config_files_em(void)
+{
+	/* Empty till config file support added to EM */
+}
+
+void
+em_free_routes(void)
+{
+	/* Empty till config file support added to EM */
+}
+
 /* Initialize exact match (hash) parameters. 8< */
 void
 setup_hash(const int socketid)
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 2110459cc3..003721c908 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -583,7 +583,7 @@ setup_fib(const int socketid)
 	struct rte_eth_dev_info dev_info;
 	struct rte_fib6_conf config;
 	struct rte_fib_conf config_ipv4;
-	unsigned int i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -603,37 +603,39 @@ setup_fib(const int socketid)
 			"Unable to create the l3fwd FIB table on socket %d\n",
 			socketid);
 
+
 	/* Populate the fib ipv4 table. */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* Skip unused ports. */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_fib_add(ipv4_l3fwd_fib_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		if (inet_ntop(AF_INET, &in, abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv4_l3fwd_route_array[i].depth,
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].depth,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv4 route added to port %d [%s]\n",
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		}
 	}
@@ -650,44 +652,50 @@ setup_fib(const int socketid)
 	config.trie.num_tbl8 = (1 << 15);
 	ipv6_l3fwd_fib_lookup_struct[socketid] = rte_fib6_create(s, socketid,
 			&config);
-	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL) {
+		free(route_base_v4);
 		rte_exit(EXIT_FAILURE,
 				"Unable to create the l3fwd FIB table on socket %d\n",
 				socketid);
+	}
 
 	/* Populate the fib IPv6 table. */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* Skip unused ports. */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv6_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_fib6_add(ipv6_l3fwd_fib_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
+			free(route_base_v6);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		if (inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip,
+		if (inet_ntop(AF_INET6, route_base_v6[i].ip_8,
 				abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv6_l3fwd_route_array[i].depth,
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].depth,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv6 route added to port %d [%s]\n",
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		}
 	}
+	free(route_base_v4);
+	free(route_base_v6);
 }
 
 /* Return ipv4 fib lookup struct. */
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index a5b476ced3..04d65eb010 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -39,6 +39,16 @@
 
 static struct rte_lpm *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 static struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
+struct lpm_route_rule *route_base_v4;
+struct lpm_route_rule *route_base_v6;
+int route_num_v4;
+int route_num_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
 
 /* Performing LPM-based lookups. 8< */
 static inline uint16_t
@@ -139,6 +149,199 @@ lpm_get_dst_port_with_ipv4(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
 #include "l3fwd_lpm.h"
 #endif
 
+static int
+parse_ipv6_addr_mask(char *token, uint32_t *ipv6, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET6, sa, ipv6) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 128, 0);
+	return 0;
+}
+
+static int
+parse_ipv4_addr_mask(char *token, uint32_t *ipv4, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET, sa, ipv4) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 32, 0);
+	*ipv4 = ntohl(*ipv4);
+	return 0;
+}
+
+static int
+lpm_parse_v6_net(char *in, uint32_t *v, uint8_t *mask_len)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = parse_ipv6_addr_mask(in, v, mask_len);
+
+	return rc;
+}
+
+static int
+lpm_parse_v6_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], v->ip_32, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_parse_v4_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = parse_ipv4_addr_mask(in[CB_FLD_DST_ADDR], &v->ip, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_add_rules(const char *rule_path,
+		struct lpm_route_rule **proute_base,
+		int (*parser)(char *, struct lpm_route_rule *))
+{
+	struct lpm_route_rule *route_rules;
+	struct lpm_route_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
+void
+lpm_free_routes(void)
+{
+	free(route_base_v4);
+	free(route_base_v6);
+	route_base_v4 = NULL;
+	route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
+}
+
 /* main processing loop */
 int
 lpm_main_loop(__rte_unused void *dummy)
@@ -548,13 +751,44 @@ lpm_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+/* Load rules from the input file */
+void
+read_config_files_lpm(void)
+{
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL) {
+		route_num_v4 = lpm_add_rules(parm_config.rule_ipv4_name,
+					&route_base_v4, &lpm_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			lpm_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "IPv4 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
+	}
+
+	/* ipv6 check */
+	if (parm_config.rule_ipv6_name != NULL) {
+		route_num_v6 = lpm_add_rules(parm_config.rule_ipv6_name,
+					&route_base_v6, &lpm_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			lpm_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "IPv6 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid route options\n");
+	}
+}
+
 void
 setup_lpm(const int socketid)
 {
 	struct rte_eth_dev_info dev_info;
 	struct rte_lpm6_config config;
 	struct rte_lpm_config config_ipv4;
-	unsigned i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -572,32 +806,33 @@ setup_lpm(const int socketid)
 			socketid);
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* skip unused ports */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			lpm_free_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
 		       inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
-		       ipv4_l3fwd_route_array[i].depth,
-		       ipv4_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v4[i].depth,
+		       route_base_v4[i].if_out, dev_info.device->name);
 	}
 
 	/* create the LPM6 table */
@@ -608,37 +843,40 @@ setup_lpm(const int socketid)
 	config.flags = 0;
 	ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid,
 				&config);
-	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL) {
+		lpm_free_routes();
 		rte_exit(EXIT_FAILURE,
 			"Unable to create the l3fwd LPM table on socket %d\n",
 			socketid);
+	}
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			lpm_free_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
-		       inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip, abuf,
+		       inet_ntop(AF_INET6, route_base_v6[i].ip_8, abuf,
 				 sizeof(abuf)),
-		       ipv6_l3fwd_route_array[i].depth,
-		       ipv6_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v6[i].depth,
+		       route_base_v6[i].if_out, dev_info.device->name);
 	}
 }
 
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index c7eba06c4d..7ddd4d9c2d 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -14,6 +14,19 @@ struct ipv6_l3fwd_route {
 	uint8_t if_out;
 };
 
-extern const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[16];
+struct lpm_route_rule {
+	union {
+		uint32_t ip;
+		union {
+			uint32_t ip_32[IPV6_ADDR_U32];
+			uint8_t ip_8[IPV6_ADDR_LEN];
+		};
+	};
+	uint8_t depth;
+	uint8_t if_out;
+};
 
-extern const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[16];
+extern struct lpm_route_rule *route_base_v4;
+extern struct lpm_route_rule *route_base_v6;
+extern int route_num_v4;
+extern int route_num_v6;
diff --git a/examples/l3fwd/lpm_default_v4.cfg b/examples/l3fwd/lpm_default_v4.cfg
new file mode 100644
index 0000000000..4db5597ed8
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD LPM
+R198.18.0.0/24 0
+R198.18.1.0/24 1
+R198.18.2.0/24 2
+R198.18.3.0/24 3
+R198.18.4.0/24 4
+R198.18.5.0/24 5
+R198.18.6.0/24 6
+R198.18.7.0/24 7
+R198.18.8.0/24 8
+R198.18.9.0/24 9
+R198.18.10.0/24 10
+R198.18.11.0/24 11
+R198.18.12.0/24 12
+R198.18.13.0/24 13
+R198.18.14.0/24 14
+R198.18.15.0/24 15
diff --git a/examples/l3fwd/lpm_default_v6.cfg b/examples/l3fwd/lpm_default_v6.cfg
new file mode 100644
index 0000000000..c50233e0ba
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD LPM
+R2001:0200:0000:0000:0000:0000:0000:0000/64 0
+R2001:0200:0000:0001:0000:0000:0000:0000/64 1
+R2001:0200:0000:0002:0000:0000:0000:0000/64 2
+R2001:0200:0000:0003:0000:0000:0000:0000/64 3
+R2001:0200:0000:0004:0000:0000:0000:0000/64 4
+R2001:0200:0000:0005:0000:0000:0000:0000/64 5
+R2001:0200:0000:0006:0000:0000:0000:0000/64 6
+R2001:0200:0000:0007:0000:0000:0000:0000/64 7
+R2001:0200:0000:0008:0000:0000:0000:0000/64 8
+R2001:0200:0000:0009:0000:0000:0000:0000/64 9
+R2001:0200:0000:000A:0000:0000:0000:0000/64 10
+R2001:0200:0000:000B:0000:0000:0000:0000/64 11
+R2001:0200:0000:000C:0000:0000:0000:0000/64 12
+R2001:0200:0000:000D:0000:0000:0000:0000/64 13
+R2001:0200:0000:000E:0000:0000:0000:0000/64 14
+R2001:0200:0000:000F:0000:0000:0000:0000/64 15
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index eb68ffc5aa..3260c88057 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -94,6 +94,8 @@ uint32_t hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 
 struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+struct parm_cfg parm_config;
+
 struct lcore_params {
 	uint16_t port_id;
 	uint8_t queue_id;
@@ -141,88 +143,65 @@ static struct rte_mempool *vector_pool[RTE_MAX_ETHPORTS];
 static uint8_t lkp_per_socket[NB_SOCKETS];
 
 struct l3fwd_lkp_mode {
+	void  (*read_config_files)(void);
 	void  (*setup)(int);
 	int   (*check_ptype)(int);
 	rte_rx_callback_fn cb_parse_ptype;
 	int   (*main_loop)(void *);
 	void* (*get_ipv4_lookup_struct)(int);
 	void* (*get_ipv6_lookup_struct)(int);
+	void  (*free_routes)(void);
 };
 
 static struct l3fwd_lkp_mode l3fwd_lkp;
 
 static struct l3fwd_lkp_mode l3fwd_em_lkp = {
+	.read_config_files		= read_config_files_em,
 	.setup                  = setup_hash,
 	.check_ptype		= em_check_ptype,
 	.cb_parse_ptype		= em_cb_parse_ptype,
 	.main_loop              = em_main_loop,
 	.get_ipv4_lookup_struct = em_get_ipv4_l3fwd_lookup_struct,
 	.get_ipv6_lookup_struct = em_get_ipv6_l3fwd_lookup_struct,
+	.free_routes			= em_free_routes,
 };
 
 static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_lpm,
 	.check_ptype		= lpm_check_ptype,
 	.cb_parse_ptype		= lpm_cb_parse_ptype,
 	.main_loop              = lpm_main_loop,
 	.get_ipv4_lookup_struct = lpm_get_ipv4_l3fwd_lookup_struct,
 	.get_ipv6_lookup_struct = lpm_get_ipv6_l3fwd_lookup_struct,
+	.free_routes			= lpm_free_routes,
 };
 
 static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_fib,
 	.check_ptype            = lpm_check_ptype,
 	.cb_parse_ptype         = lpm_cb_parse_ptype,
 	.main_loop              = fib_main_loop,
 	.get_ipv4_lookup_struct = fib_get_ipv4_l3fwd_lookup_struct,
 	.get_ipv6_lookup_struct = fib_get_ipv6_l3fwd_lookup_struct,
+	.free_routes			= lpm_free_routes,
 };
 
 /*
- * 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
- * 198.18.{0-15}.0/24 = Port {0-15}
+ * API's called during initialization to setup ACL/EM/LPM rules.
  */
-const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[] = {
-	{RTE_IPV4(198, 18, 0, 0), 24, 0},
-	{RTE_IPV4(198, 18, 1, 0), 24, 1},
-	{RTE_IPV4(198, 18, 2, 0), 24, 2},
-	{RTE_IPV4(198, 18, 3, 0), 24, 3},
-	{RTE_IPV4(198, 18, 4, 0), 24, 4},
-	{RTE_IPV4(198, 18, 5, 0), 24, 5},
-	{RTE_IPV4(198, 18, 6, 0), 24, 6},
-	{RTE_IPV4(198, 18, 7, 0), 24, 7},
-	{RTE_IPV4(198, 18, 8, 0), 24, 8},
-	{RTE_IPV4(198, 18, 9, 0), 24, 9},
-	{RTE_IPV4(198, 18, 10, 0), 24, 10},
-	{RTE_IPV4(198, 18, 11, 0), 24, 11},
-	{RTE_IPV4(198, 18, 12, 0), 24, 12},
-	{RTE_IPV4(198, 18, 13, 0), 24, 13},
-	{RTE_IPV4(198, 18, 14, 0), 24, 14},
-	{RTE_IPV4(198, 18, 15, 0), 24, 15},
-};
+static void
+l3fwd_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
 
-/*
- * 2001:200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180).
- * 2001:200:0:{0-f}::/64 = Port {0-15}
- */
-const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[] = {
-	{{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 0},
-	{{32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 1},
-	{{32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 2},
-	{{32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 3},
-	{{32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 4},
-	{{32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 5},
-	{{32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 6},
-	{{32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 7},
-	{{32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 8},
-	{{32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 9},
-	{{32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 10},
-	{{32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 11},
-	{{32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 12},
-	{{32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 13},
-	{{32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 14},
-	{{32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 15},
-};
+static void
+l3fwd_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
 
 /*
  * Setup lookup methods for forwarding.
@@ -339,6 +318,8 @@ print_usage(const char *prgname)
 {
 	fprintf(stderr, "%s [EAL options] --"
 		" -p PORTMASK"
+		"  --rule_ipv4=FILE"
+		"  --rule_ipv6=FILE"
 		" [-P]"
 		" [--lookup]"
 		" --config (port,queue,lcore)[,(port,queue,lcore)]"
@@ -381,7 +362,10 @@ print_usage(const char *prgname)
 		"  --event-vector-size: Max vector size if event vectorization is enabled.\n"
 		"  --event-vector-tmo: Max timeout to form vector in nanoseconds if event vectorization is enabled\n"
 		"  -E : Enable exact match, legacy flag please use --lookup=em instead\n"
-		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n\n",
+		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n"
+		"  --rule_ipv4=FILE: Specify the ipv4 rules entries file.\n"
+		"                    Each rule occupies one line.\n"
+		"  --rule_ipv6=FILE: Specify the ipv6 rules entries file.\n\n",
 		prgname);
 }
 
@@ -596,6 +580,8 @@ static const char short_options[] =
 #define CMD_LINE_OPT_ENABLE_VECTOR "event-vector"
 #define CMD_LINE_OPT_VECTOR_SIZE "event-vector-size"
 #define CMD_LINE_OPT_VECTOR_TMO_NS "event-vector-tmo"
+#define CMD_LINE_OPT_RULE_IPV4 "rule_ipv4"
+#define CMD_LINE_OPT_RULE_IPV6 "rule_ipv6"
 
 enum {
 	/* long options mapped to a short option */
@@ -610,6 +596,8 @@ enum {
 	CMD_LINE_OPT_MAX_PKT_LEN_NUM,
 	CMD_LINE_OPT_HASH_ENTRY_NUM_NUM,
 	CMD_LINE_OPT_PARSE_PTYPE_NUM,
+	CMD_LINE_OPT_RULE_IPV4_NUM,
+	CMD_LINE_OPT_RULE_IPV6_NUM,
 	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
 	CMD_LINE_OPT_MODE_NUM,
 	CMD_LINE_OPT_EVENTQ_SYNC_NUM,
@@ -637,6 +625,8 @@ static const struct option lgopts[] = {
 	{CMD_LINE_OPT_ENABLE_VECTOR, 0, 0, CMD_LINE_OPT_ENABLE_VECTOR_NUM},
 	{CMD_LINE_OPT_VECTOR_SIZE, 1, 0, CMD_LINE_OPT_VECTOR_SIZE_NUM},
 	{CMD_LINE_OPT_VECTOR_TMO_NS, 1, 0, CMD_LINE_OPT_VECTOR_TMO_NS_NUM},
+	{CMD_LINE_OPT_RULE_IPV4,   1, 0, CMD_LINE_OPT_RULE_IPV4_NUM},
+	{CMD_LINE_OPT_RULE_IPV6,   1, 0, CMD_LINE_OPT_RULE_IPV6_NUM},
 	{NULL, 0, 0, 0}
 };
 
@@ -791,6 +781,12 @@ parse_args(int argc, char **argv)
 		case CMD_LINE_OPT_VECTOR_TMO_NS_NUM:
 			evt_rsrc->vector_tmo_ns = strtoull(optarg, NULL, 10);
 			break;
+		case CMD_LINE_OPT_RULE_IPV4_NUM:
+			l3fwd_set_rule_ipv4_name(optarg);
+			break;
+		case CMD_LINE_OPT_RULE_IPV6_NUM:
+			l3fwd_set_rule_ipv6_name(optarg);
+			break;
 		default:
 			print_usage(prgname);
 			return -1;
@@ -1358,6 +1354,24 @@ l3fwd_event_service_setup(void)
 	}
 }
 
+/* Bypass comment and empty lines */
+int
+is_bypass_line(const char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
 int
 main(int argc, char **argv)
 {
@@ -1395,6 +1409,9 @@ main(int argc, char **argv)
 	/* Setup function pointers for lookup method. */
 	setup_l3fwd_lookup_tables();
 
+	/* Add the config file rules */
+	l3fwd_lkp.read_config_files();
+
 	evt_rsrc->per_port_pool = per_port_pool;
 	evt_rsrc->pkt_pool = pktmbuf_pool;
 	evt_rsrc->vec_pool = vector_pool;
@@ -1501,6 +1518,9 @@ main(int argc, char **argv)
 		}
 	}
 
+	/* clean up config file routes */
+	l3fwd_lkp.free_routes();
+
 	/* clean up the EAL */
 	rte_eal_cleanup();
 
-- 
2.25.1


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

* [PATCH v4 2/2] examples/l3fwd: add config file support for EM
  2022-01-26 12:44     ` [PATCH v4 0/2] Add config file support for l3fwd Sean Morrissey
  2022-01-26 12:44       ` [PATCH v4 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
@ 2022-01-26 12:44       ` Sean Morrissey
  2022-02-04 19:59       ` [PATCH v5 0/2] Add config file support for l3fwd Sean Morrissey
  2 siblings, 0 replies; 46+ messages in thread
From: Sean Morrissey @ 2022-01-26 12:44 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Ravi Kerur, Konstantin Ananyev

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for EM with a format
similar to l3fwd-acl one.

With the removal of the hardcoded route tables for IPv4
and IPv6 from 'l3fwd_em', these routes have been moved
to a separate default config file for use with EM.

Related l3fwd docs have been updated to relfect these
changes.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Signed-off-by: Ravi Kerur <rkerur@gmail.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
 examples/l3fwd/em_default_v4.cfg        |  17 +
 examples/l3fwd/em_default_v6.cfg        |  17 +
 examples/l3fwd/l3fwd_em.c               | 476 ++++++++++++++----------
 examples/l3fwd/l3fwd_route.h            |  38 +-
 5 files changed, 411 insertions(+), 226 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg

diff --git a/doc/guides/sample_app_ug/l3_forward.rst b/doc/guides/sample_app_ug/l3_forward.rst
index 6d7d7c5cc1..01d86db95d 100644
--- a/doc/guides/sample_app_ug/l3_forward.rst
+++ b/doc/guides/sample_app_ug/l3_forward.rst
@@ -47,6 +47,7 @@ and loaded into the LPM or FIB object at initialization time.
 In the sample application, hash-based and FIB-based forwarding supports
 both IPv4 and IPv6.
 LPM-based forwarding supports IPv4 only.
+During the initialization phase route rules for IPv4 and IPv6 are read from rule files.
 
 Compiling the Application
 -------------------------
@@ -61,6 +62,8 @@ Running the Application
 The application has a number of command line options::
 
     ./dpdk-l3fwd [EAL options] -- -p PORTMASK
+                             --rule_ipv4=FILE
+                             --rule_ipv6=FILE
                              [-P]
                              [--lookup LOOKUP_METHOD]
                              --config(port,queue,lcore)[,(port,queue,lcore)]
@@ -82,6 +85,11 @@ Where,
 
 * ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
 
+* ``--rule_ipv4=FILE:`` specify the ipv4 rules entries file.
+  Each rule occupies one line.
+
+* ``--rule_ipv6=FILE:`` specify the ipv6 rules entries file.
+
 * ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
   Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
 
@@ -135,7 +143,7 @@ To enable L3 forwarding between two ports, assuming that both ports are in the s
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
+    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)" --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -157,19 +165,23 @@ In this command:
 |          |           |           |                                     |
 +----------+-----------+-----------+-------------------------------------+
 
+*   The -rule_ipv4 option specifies the reading of IPv4 rules sets from the rule_ipv4.cfg file
+
+*   The -rule_ipv6 option specifies the reading of IPv6 rules sets from the rule_ipv6.cfg file.
+
 To use eventdev mode with sync method **ordered** on above mentioned environment,
 Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 or
 
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> \
-		-- -p 0x03 --mode=eventdev --eventq-sched=ordered
+		-- -p 0x03 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -192,7 +204,7 @@ scheduler. Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In case of eventdev mode, *--config* option is not used for ethernet port
 configuration. Instead each ethernet port will be configured with mentioned
@@ -216,6 +228,49 @@ The following sections provide some explanation of the sample application code.
 the initialization and run-time paths are very similar to those of the :doc:`l2_forward_real_virtual` and :doc:`l2_forward_event`.
 The following sections describe aspects that are specific to the L3 Forwarding sample application.
 
+Parse Rules from File
+~~~~~~~~~~~~~~~~~~~~~
+
+The application parses the rules from the file and adds them to the appropriate route table by calling the appropriate function.
+It ignores empty and comment lines, and parses and validates the rules it reads.
+If errors are detected, the application exits with messages to identify the errors encountered.
+
+The format of the route rules differs based on which lookup method is being used.
+Therefore, the code only decreases the priority number with each rule it parses.
+Route rules are mandatory.
+To read data from the specified file successfully, the application assumes the following:
+
+*   Each rule occupies a single line.
+
+*   Only the following four rule line types are valid in this application:
+
+*   Route rule line, which starts with a leading character 'R'
+
+*   Comment line, which starts with a leading character '#'
+
+*   Empty line, which consists of a space, form-feed ('\f'), newline ('\n'),
+    carriage return ('\r'), horizontal tab ('\t'), or vertical tab ('\v').
+
+Other lines types are considered invalid.
+
+*   Rules are organized in descending order of priority,
+    which means rules at the head of the file always have a higher priority than those further down in the file.
+
+*   A typical IPv4 LPM/FIB rule line should have a format as shown below:
+
+R<destination_ip>/<ip_mask_length><output_port_number>
+
+*   A typical IPv4 EM rule line should have a format as shown below:
+
+R<destination_ip><source_ip><destination_port><source_port><protocol><output_port_number>
+
+IPv4 addresses are specified in CIDR format as specified in RFC 4632.
+For LPM/FIB they consist of the dot notation for the address and a prefix length separated by '/'.
+For example, 192.168.0.34/32, where the address is 192.168.0.34 and the prefix length is 32.
+For EM they consist of just the dot notation for the address and no prefix length.
+For example, 192.168.0.34, where the Address is 192.168.0.34.
+EM also includes ports which are specified as a single number which represents a single port.
+
 Hash Initialization
 ~~~~~~~~~~~~~~~~~~~
 
@@ -227,8 +282,6 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
 
     The Hash initialization will setup both ipv4 and ipv6 hash table,
     and populate the either table depending on the value of variable ipv6.
-    To support the hash performance test with up to 8M single direction flows/16M bi-direction flows,
-    populate_ipv4_many_flow_into_table() function will populate the hash table with specified hash table entry number(default 4M).
 
 .. note::
 
@@ -246,22 +299,14 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
         {
             // ...
 
-            if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_many_flow_into_table(ipv4_l3fwd_lookup_struct[socketid], hash_entry_number);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_many_flow_into_table( ipv6_l3fwd_lookup_struct[socketid], hash_entry_number);
-                }
-            } else
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_few_flow_into_table(ipv4_l3fwd_lookup_struct[socketid]);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_few_flow_into_table(ipv6_l3fwd_lookup_struct[socketid]);
-                }
+            if (ipv6 == 0) {
+                /* populate the ipv4 hash */
+                populate_ipv4_flow_into_table(
+                    ipv4_l3fwd_em_lookup_struct[socketid]);
+            } else {
+                /* populate the ipv6 hash */
+                populate_ipv6_flow_into_table(
+                    ipv6_l3fwd_em_lookup_struct[socketid]);
             }
         }
     #endif
diff --git a/examples/l3fwd/em_default_v4.cfg b/examples/l3fwd/em_default_v4.cfg
new file mode 100644
index 0000000000..b70bdb6601
--- /dev/null
+++ b/examples/l3fwd/em_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD EM
+R198.18.0.0 198.18.0.1 9 9 0x11 0
+R198.18.1.0 198.18.1.1 9 9 0x11 1
+R198.18.2.0 198.18.2.1 9 9 0x11 2
+R198.18.3.0 198.18.3.1 9 9 0x11 3
+R198.18.4.0 198.18.4.1 9 9 0x11 4
+R198.18.5.0 198.18.5.1 9 9 0x11 5
+R198.18.6.0 198.18.6.1 9 9 0x11 6
+R198.18.7.0 198.18.7.1 9 9 0x11 7
+R198.18.8.0 198.18.8.1 9 9 0x11 8
+R198.18.9.0 198.18.9.1 9 9 0x11 9
+R198.18.10.0 198.18.10.1 9 9 0x11 10
+R198.18.11.0 198.18.11.1 9 9 0x11 11
+R198.18.12.0 198.18.12.1 9 9 0x11 12
+R198.18.13.0 198.18.13.1 9 9 0x11 13
+R198.18.14.0 198.18.14.1 9 9 0x11 14
+R198.18.15.0 198.18.15.1 9 9 0x11 15
diff --git a/examples/l3fwd/em_default_v6.cfg b/examples/l3fwd/em_default_v6.cfg
new file mode 100644
index 0000000000..0b94f8d8d2
--- /dev/null
+++ b/examples/l3fwd/em_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD EM
+R2001:0200:0000:0000:0000:0000:0000:0000 2001:0200:0000:0000:0000:0000:0000:0001 9 9 0x11 0
+R2001:0200:0000:0001:0000:0000:0000:0000 2001:0200:0000:0001:0000:0000:0000:0001 9 9 0x11 1
+R2001:0200:0000:0002:0000:0000:0000:0000 2001:0200:0000:0002:0000:0000:0000:0001 9 9 0x11 2
+R2001:0200:0000:0003:0000:0000:0000:0000 2001:0200:0000:0003:0000:0000:0000:0001 9 9 0x11 3
+R2001:0200:0000:0004:0000:0000:0000:0000 2001:0200:0000:0004:0000:0000:0000:0001 9 9 0x11 4
+R2001:0200:0000:0005:0000:0000:0000:0000 2001:0200:0000:0005:0000:0000:0000:0001 9 9 0x11 5
+R2001:0200:0000:0006:0000:0000:0000:0000 2001:0200:0000:0006:0000:0000:0000:0001 9 9 0x11 6
+R2001:0200:0000:0007:0000:0000:0000:0000 2001:0200:0000:0007:0000:0000:0000:0001 9 9 0x11 7
+R2001:0200:0000:0008:0000:0000:0000:0000 2001:0200:0000:0008:0000:0000:0000:0001 9 9 0x11 8
+R2001:0200:0000:0009:0000:0000:0000:0000 2001:0200:0000:0009:0000:0000:0000:0001 9 9 0x11 9
+R2001:0200:0000:000A:0000:0000:0000:0000 2001:0200:0000:000A:0000:0000:0000:0001 9 9 0x11 10
+R2001:0200:0000:000B:0000:0000:0000:0000 2001:0200:0000:000B:0000:0000:0000:0001 9 9 0x11 11
+R2001:0200:0000:000C:0000:0000:0000:0000 2001:0200:0000:000C:0000:0000:0000:0001 9 9 0x11 12
+R2001:0200:0000:000D:0000:0000:0000:0000 2001:0200:0000:000D:0000:0000:0000:0001 9 9 0x11 13
+R2001:0200:0000:000E:0000:0000:0000:0000 2001:0200:0000:000E:0000:0000:0000:0001 9 9 0x11 14
+R2001:0200:0000:000F:0000:0000:0000:0000 2001:0200:0000:000F:0000:0000:0000:0001 9 9 0x11 15
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 9058e75a76..ab540f313e 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -27,6 +27,7 @@
 
 #include "l3fwd.h"
 #include "l3fwd_event.h"
+#include "l3fwd_route.h"
 
 #if defined(RTE_ARCH_X86) || defined(__ARM_FEATURE_CRC32)
 #define EM_HASH_CRC 1
@@ -42,13 +43,18 @@
 
 #define IPV6_ADDR_LEN 16
 
-struct ipv4_5tuple {
-	uint32_t ip_dst;
-	uint32_t ip_src;
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
+static struct em_rule *em_route_base_v4;
+static struct em_rule *em_route_base_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_SRC_ADDR,
+	CB_FLD_DST_PORT,
+	CB_FLD_SRC_PORT,
+	CB_FLD_PROTO,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
 
 union ipv4_5tuple_host {
 	struct {
@@ -65,14 +71,6 @@ union ipv4_5tuple_host {
 
 #define XMM_NUM_IN_IPV6_5TUPLE 3
 
-struct ipv6_5tuple {
-	uint8_t  ip_dst[IPV6_ADDR_LEN];
-	uint8_t  ip_src[IPV6_ADDR_LEN];
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
-
 union ipv6_5tuple_host {
 	struct {
 		uint16_t pad0;
@@ -87,8 +85,6 @@ union ipv6_5tuple_host {
 	xmm_t xmm[XMM_NUM_IN_IPV6_5TUPLE];
 };
 
-
-
 struct ipv4_l3fwd_em_route {
 	struct ipv4_5tuple key;
 	uint8_t if_out;
@@ -99,66 +95,6 @@ struct ipv6_l3fwd_em_route {
 	uint8_t if_out;
 };
 
-/* 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
- * Use RFC863 Discard Protocol.
- */
-static const struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[] = {
-	{{RTE_IPV4(198, 18, 0, 0), RTE_IPV4(198, 18, 0, 1),  9, 9, IPPROTO_UDP}, 0},
-	{{RTE_IPV4(198, 18, 1, 0), RTE_IPV4(198, 18, 1, 1),  9, 9, IPPROTO_UDP}, 1},
-	{{RTE_IPV4(198, 18, 2, 0), RTE_IPV4(198, 18, 2, 1),  9, 9, IPPROTO_UDP}, 2},
-	{{RTE_IPV4(198, 18, 3, 0), RTE_IPV4(198, 18, 3, 1),  9, 9, IPPROTO_UDP}, 3},
-	{{RTE_IPV4(198, 18, 4, 0), RTE_IPV4(198, 18, 4, 1),  9, 9, IPPROTO_UDP}, 4},
-	{{RTE_IPV4(198, 18, 5, 0), RTE_IPV4(198, 18, 5, 1),  9, 9, IPPROTO_UDP}, 5},
-	{{RTE_IPV4(198, 18, 6, 0), RTE_IPV4(198, 18, 6, 1),  9, 9, IPPROTO_UDP}, 6},
-	{{RTE_IPV4(198, 18, 7, 0), RTE_IPV4(198, 18, 7, 1),  9, 9, IPPROTO_UDP}, 7},
-	{{RTE_IPV4(198, 18, 8, 0), RTE_IPV4(198, 18, 8, 1),  9, 9, IPPROTO_UDP}, 8},
-	{{RTE_IPV4(198, 18, 9, 0), RTE_IPV4(198, 18, 9, 1),  9, 9, IPPROTO_UDP}, 9},
-	{{RTE_IPV4(198, 18, 10, 0), RTE_IPV4(198, 18, 10, 1),  9, 9, IPPROTO_UDP}, 10},
-	{{RTE_IPV4(198, 18, 11, 0), RTE_IPV4(198, 18, 11, 1),  9, 9, IPPROTO_UDP}, 11},
-	{{RTE_IPV4(198, 18, 12, 0), RTE_IPV4(198, 18, 12, 1),  9, 9, IPPROTO_UDP}, 12},
-	{{RTE_IPV4(198, 18, 13, 0), RTE_IPV4(198, 18, 13, 1),  9, 9, IPPROTO_UDP}, 13},
-	{{RTE_IPV4(198, 18, 14, 0), RTE_IPV4(198, 18, 14, 1),  9, 9, IPPROTO_UDP}, 14},
-	{{RTE_IPV4(198, 18, 15, 0), RTE_IPV4(198, 18, 15, 1),  9, 9, IPPROTO_UDP}, 15},
-};
-
-/* 2001:0200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180).
- * Use RFC863 Discard Protocol.
- */
-static const struct ipv6_l3fwd_em_route ipv6_l3fwd_em_route_array[] = {
-	{{{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 0},
-	{{{32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 1},
-	{{{32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 2},
-	{{{32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 3},
-	{{{32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 4},
-	{{{32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 5},
-	{{{32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 6},
-	{{{32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 7},
-	{{{32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 8},
-	{{{32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 9},
-	{{{32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 10},
-	{{{32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 11},
-	{{{32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 12},
-	{{{32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 13},
-	{{{32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 14},
-	{{{32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0},
-	  {32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 15},
-};
-
 struct rte_hash *ipv4_l3fwd_em_lookup_struct[NB_SOCKETS];
 struct rte_hash *ipv6_l3fwd_em_lookup_struct[NB_SOCKETS];
 
@@ -235,10 +171,6 @@ ipv6_hash_crc(const void *data, __rte_unused uint32_t data_len,
 	return init_val;
 }
 
-#define IPV4_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv4_l3fwd_em_route_array)
-
-#define IPV6_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv6_l3fwd_em_route_array)
-
 static uint8_t ipv4_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 static uint8_t ipv6_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 
@@ -346,6 +278,174 @@ em_get_ipv6_dst_port(void *ipv6_hdr, uint16_t portid, void *lookup_struct)
 #include "l3fwd_em.h"
 #endif
 
+static int
+em_parse_v6_net(const char *in, uint8_t *v)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = inet_pton(AF_INET6, in, v);
+	if (rc != 1)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+em_parse_v6_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], v->v6_key.ip_dst);
+	if (rc != 0)
+		return rc;
+	rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], v->v6_key.ip_src);
+	if (rc != 0)
+		return rc;
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_parse_v4_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = inet_pton(AF_INET, in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
+	v->v4_key.ip_dst = ntohl(v->v4_key.ip_dst);
+	if (rc != 1)
+		return rc;
+
+	rc = inet_pton(AF_INET, in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
+	v->v4_key.ip_src = ntohl(v->v4_key.ip_src);
+	if (rc != 1)
+		return rc;
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_add_rules(const char *rule_path,
+		struct em_rule **proute_base,
+		int (*parser)(char *, struct em_rule *))
+{
+	struct em_rule *route_rules;
+	struct em_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
 static void
 convert_ipv4_5tuple(struct ipv4_5tuple *key1,
 		union ipv4_5tuple_host *key2)
@@ -382,122 +482,98 @@ convert_ipv6_5tuple(struct ipv6_5tuple *key1,
 #define BIT_8_TO_15 0x0000ff00
 
 static inline void
-populate_ipv4_few_flow_into_table(const struct rte_hash *h)
+populate_ipv4_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
-	for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv4_l3fwd_em_route  entry;
+	for (i = 0; i < route_num_v4; i++) {
+		struct em_rule *entry;
 		union ipv4_5tuple_host newkey;
+		struct in_addr src;
+		struct in_addr dst;
 
-		entry = ipv4_l3fwd_em_route_array[i];
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v4[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v4[i];
+		convert_ipv4_5tuple(&(entry->v4_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv4_l3fwd_out_if[ret] = entry.if_out;
+		ipv4_l3fwd_out_if[ret] = entry->if_out;
+		rte_eth_dev_info_get(em_route_base_v4[i].if_out,
+				     &dev_info);
+
+		src.s_addr = htonl(em_route_base_v4[i].v4_key.ip_src);
+		dst.s_addr = htonl(em_route_base_v4[i].v4_key.ip_dst);
+		printf("EM: Adding route %s, %s, %d, %d, %d (%d) [%s]\n",
+			   inet_ntop(AF_INET, &dst, dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET, &src, srcbuf, sizeof(srcbuf)),
+			   em_route_base_v4[i].v4_key.port_dst,
+			   em_route_base_v4[i].v4_key.port_src,
+			   em_route_base_v4[i].v4_key.proto,
+		       em_route_base_v4[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 " keys\n",
-		(uint64_t)IPV4_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v4);
 }
 
 #define BIT_16_TO_23 0x00ff0000
 static inline void
-populate_ipv6_few_flow_into_table(const struct rte_hash *h)
+populate_ipv6_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
-	for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv6_l3fwd_em_route entry;
+	for (i = 0; i < route_num_v6; i++) {
+		struct em_rule *entry;
 		union ipv6_5tuple_host newkey;
 
-		entry = ipv6_l3fwd_em_route_array[i];
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v6[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v6[i];
+		convert_ipv6_5tuple(&(entry->v6_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv6_l3fwd_out_if[ret] = entry.if_out;
+		ipv6_l3fwd_out_if[ret] = entry->if_out;
+		rte_eth_dev_info_get(em_route_base_v6[i].if_out,
+				     &dev_info);
+
+		printf("EM: Adding route %s, %s, %d, %d, %d (%d) [%s]\n",
+			   inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip32_dst,
+			   dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip32_src,
+			   srcbuf, sizeof(srcbuf)),
+			   em_route_base_v6[i].v6_key.port_dst,
+			   em_route_base_v6[i].v6_key.port_src,
+			   em_route_base_v6[i].v6_key.proto,
+		       em_route_base_v6[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 "keys\n",
-		(uint64_t)IPV6_L3FWD_EM_NUM_ROUTES);
-}
-
-#define NUMBER_PORT_USED 16
-static inline void
-populate_ipv4_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv4_l3fwd_em_route entry;
-		union ipv4_5tuple_host newkey;
-
-		uint8_t a = (uint8_t)((port + 1) % BYTE_VALUE_MAX);
-
-		/* Create the ipv4 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv4_l3fwd_em_route_array[port];
-		entry.key.ip_dst = RTE_IPV4(198, 18, port, a);
-		convert_ipv4_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv4_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
-}
-
-static inline void
-populate_ipv6_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv6_l3fwd_em_route entry;
-		union ipv6_5tuple_host newkey;
-
-		/* Create the ipv6 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv6_l3fwd_em_route_array[port];
-		entry.key.ip_dst[15] = (port + 1) % BYTE_VALUE_MAX;
-		convert_ipv6_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv6_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
+		(uint64_t)route_num_v6);
 }
 
 /* Requirements:
@@ -972,17 +1048,46 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
-/* Load rules from the input file */
 void
-read_config_files_em(void)
+em_free_routes(void)
 {
-	/* Empty till config file support added to EM */
+	free(em_route_base_v4);
+	free(em_route_base_v6);
+	em_route_base_v4 = NULL;
+	em_route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
 }
 
+/* Load rules from the input file */
 void
-em_free_routes(void)
+read_config_files_em(void)
 {
-	/* Empty till config file support added to EM */
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL) {
+		route_num_v4 = em_add_rules(parm_config.rule_ipv4_name,
+					&em_route_base_v4, &em_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			em_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv4 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "EM IPv4 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+	}
+
+	/* ipv6 check */
+	if (parm_config.rule_ipv6_name != NULL) {
+		route_num_v6 = em_add_rules(parm_config.rule_ipv6_name,
+					&em_route_base_v6, &em_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			em_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(ERR, L3FWD, "EM IPv6 rule file not specified\n");
+		rte_exit(EXIT_FAILURE, "Failed to get valid EM options\n");
+	}
 }
 
 /* Initialize exact match (hash) parameters. 8< */
@@ -1029,35 +1134,18 @@ setup_hash(const int socketid)
 			"Unable to create the l3fwd hash on socket %d\n",
 			socketid);
 
-	if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-		/* For testing hash matching with a large number of flows we
-		 * generate millions of IP 5-tuples with an incremented dst
-		 * address to initialize the hash table. */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_many_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_many_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		}
+	/*
+	 * Use data from ipv4/ipv6 l3fwd config file
+	 * directly to initialize the hash table.
+	 */
+	if (ipv6 == 0) {
+		/* populate the ipv4 hash */
+		populate_ipv4_flow_into_table(
+			ipv4_l3fwd_em_lookup_struct[socketid]);
 	} else {
-		/*
-		 * Use data in ipv4/ipv6 l3fwd lookup table
-		 * directly to initialize the hash table.
-		 */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_few_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid]);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_few_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid]);
-		}
+		/* populate the ipv6 hash */
+		populate_ipv6_flow_into_table(
+			ipv6_l3fwd_em_lookup_struct[socketid]);
 	}
 }
 /* >8 End of initialization of hash parameters. */
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index 7ddd4d9c2d..ce62aa9b3e 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -2,17 +2,27 @@
  * Copyright(c) 2021 Intel Corporation
  */
 
-struct ipv4_l3fwd_route {
-	uint32_t ip;
-	uint8_t  depth;
-	uint8_t  if_out;
-};
+struct ipv4_5tuple {
+	uint32_t ip_dst;
+	uint32_t ip_src;
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
 
-struct ipv6_l3fwd_route {
-	uint8_t ip[16];
-	uint8_t depth;
-	uint8_t if_out;
-};
+struct ipv6_5tuple {
+	union {
+		uint8_t  ip_dst[IPV6_ADDR_LEN];
+		uint32_t ip32_dst[IPV6_ADDR_U32];
+	};
+	union {
+		uint8_t  ip_src[IPV6_ADDR_LEN];
+		uint32_t ip32_src[IPV6_ADDR_U32];
+	};
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
 
 struct lpm_route_rule {
 	union {
@@ -26,6 +36,14 @@ struct lpm_route_rule {
 	uint8_t if_out;
 };
 
+struct em_rule {
+		union {
+		struct ipv4_5tuple v4_key;
+		struct ipv6_5tuple v6_key;
+	};
+	uint8_t if_out;
+};
+
 extern struct lpm_route_rule *route_base_v4;
 extern struct lpm_route_rule *route_base_v6;
 extern int route_num_v4;
-- 
2.25.1


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

* [PATCH v5 0/2] Add config file support for l3fwd
  2022-01-26 12:44     ` [PATCH v4 0/2] Add config file support for l3fwd Sean Morrissey
  2022-01-26 12:44       ` [PATCH v4 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
  2022-01-26 12:44       ` [PATCH v4 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
@ 2022-02-04 19:59       ` Sean Morrissey
  2022-02-04 19:59         ` [PATCH v5 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
                           ` (3 more replies)
  2 siblings, 4 replies; 46+ messages in thread
From: Sean Morrissey @ 2022-02-04 19:59 UTC (permalink / raw)
  Cc: dev, Sean Morrissey

This patchset introduces config file support for l3fwd
and its lookup methods LPM, FIB, and EM, similar to
that of l3fwd-acl. This allows for route rules to be
defined in configuration files and edited there instead
of in each of the lookup methods hardcoded route tables.

V4:
* Fix nondeterministic bug of segfault on termination of
  sample app.
V5:
* Reintroduce hardcoded tables as to not break dts and
  allow for hardcoded tables to be used if no config
  files presented.

Sean Morrissey (2):
  examples/l3fwd: add config file support for LPM/FIB
  examples/l3fwd: add config file support for EM

 doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
 examples/l3fwd/em_default_v4.cfg        |  17 +
 examples/l3fwd/em_default_v6.cfg        |  17 +
 examples/l3fwd/l3fwd.h                  |  41 +++
 examples/l3fwd/l3fwd_em.c               | 471 +++++++++++++++++-------
 examples/l3fwd/l3fwd_fib.c              |  50 +--
 examples/l3fwd/l3fwd_lpm.c              | 315 +++++++++++++++-
 examples/l3fwd/l3fwd_route.h            |  41 +++
 examples/l3fwd/lpm_default_v4.cfg       |  17 +
 examples/l3fwd/lpm_default_v6.cfg       |  17 +
 examples/l3fwd/main.c                   |  68 +++-
 11 files changed, 949 insertions(+), 194 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg

-- 
2.25.1


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

* [PATCH v5 1/2] examples/l3fwd: add config file support for LPM/FIB
  2022-02-04 19:59       ` [PATCH v5 0/2] Add config file support for l3fwd Sean Morrissey
@ 2022-02-04 19:59         ` Sean Morrissey
  2022-02-08  2:21           ` Han, YingyaX
  2022-02-04 19:59         ` [PATCH v5 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
                           ` (2 subsequent siblings)
  3 siblings, 1 reply; 46+ messages in thread
From: Sean Morrissey @ 2022-02-04 19:59 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Konstantin Ananyev

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for LPM and FIB,
with format similar to l3fwd-acl one.

Users can now use the default hardcoded route tables
or optionally config files. Default config files have
been provided for use with LPM and FIB.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 examples/l3fwd/l3fwd.h            |  41 ++++
 examples/l3fwd/l3fwd_em.c         |  13 ++
 examples/l3fwd/l3fwd_fib.c        |  50 ++---
 examples/l3fwd/l3fwd_lpm.c        | 315 ++++++++++++++++++++++++++++--
 examples/l3fwd/l3fwd_route.h      |  17 ++
 examples/l3fwd/lpm_default_v4.cfg |  17 ++
 examples/l3fwd/lpm_default_v6.cfg |  17 ++
 examples/l3fwd/main.c             |  68 ++++++-
 8 files changed, 495 insertions(+), 43 deletions(-)
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg

diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 38ca19133c..4e1e0af033 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -58,6 +58,30 @@
 #endif
 #define HASH_ENTRY_NUMBER_DEFAULT	16
 
+/*Log file related character defs. */
+#define COMMENT_LEAD_CHAR	('#')
+#define ROUTE_LEAD_CHAR		('R')
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
+struct parm_cfg {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+};
+
 struct mbuf_table {
 	uint16_t len;
 	struct rte_mbuf *m_table[MAX_PKT_BURST];
@@ -96,6 +120,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern struct parm_cfg parm_config;
+
 /* Send burst of packets on an output interface */
 static inline int
 send_burst(struct lcore_conf *qconf, uint16_t n, uint16_t port)
@@ -183,6 +209,12 @@ int
 init_mem(uint16_t portid, unsigned int nb_mbuf);
 
 /* Function pointers for LPM, EM or FIB functionality. */
+void
+read_config_files_lpm(void);
+
+void
+read_config_files_em(void);
+
 void
 setup_lpm(const int socketid);
 
@@ -286,4 +318,13 @@ fib_get_ipv4_l3fwd_lookup_struct(const int socketid);
 void *
 fib_get_ipv6_l3fwd_lookup_struct(const int socketid);
 
+void
+em_free_routes(void);
+
+void
+lpm_free_routes(void);
+
+int
+is_bypass_line(const char *buff);
+
 #endif  /* __L3_FWD_H__ */
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 5cc4a4d979..9058e75a76 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -972,6 +972,19 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+/* Load rules from the input file */
+void
+read_config_files_em(void)
+{
+	/* Empty till config file support added to EM */
+}
+
+void
+em_free_routes(void)
+{
+	/* Empty till config file support added to EM */
+}
+
 /* Initialize exact match (hash) parameters. 8< */
 void
 setup_hash(const int socketid)
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 2110459cc3..6e0054b4cb 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -583,7 +583,7 @@ setup_fib(const int socketid)
 	struct rte_eth_dev_info dev_info;
 	struct rte_fib6_conf config;
 	struct rte_fib_conf config_ipv4;
-	unsigned int i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -603,37 +603,39 @@ setup_fib(const int socketid)
 			"Unable to create the l3fwd FIB table on socket %d\n",
 			socketid);
 
+
 	/* Populate the fib ipv4 table. */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* Skip unused ports. */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_fib_add(ipv4_l3fwd_fib_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		if (inet_ntop(AF_INET, &in, abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv4_l3fwd_route_array[i].depth,
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].depth,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv4 route added to port %d [%s]\n",
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		}
 	}
@@ -650,41 +652,45 @@ setup_fib(const int socketid)
 	config.trie.num_tbl8 = (1 << 15);
 	ipv6_l3fwd_fib_lookup_struct[socketid] = rte_fib6_create(s, socketid,
 			&config);
-	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL) {
+		free(route_base_v4);
 		rte_exit(EXIT_FAILURE,
 				"Unable to create the l3fwd FIB table on socket %d\n",
 				socketid);
+	}
 
 	/* Populate the fib IPv6 table. */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* Skip unused ports. */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv6_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_fib6_add(ipv6_l3fwd_fib_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
+			free(route_base_v6);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		if (inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip,
+		if (inet_ntop(AF_INET6, route_base_v6[i].ip_8,
 				abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv6_l3fwd_route_array[i].depth,
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].depth,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv6 route added to port %d [%s]\n",
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		}
 	}
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index a5b476ced3..9e24ad1e7e 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -39,6 +39,16 @@
 
 static struct rte_lpm *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 static struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
+struct lpm_route_rule *route_base_v4;
+struct lpm_route_rule *route_base_v6;
+int route_num_v4;
+int route_num_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
 
 /* Performing LPM-based lookups. 8< */
 static inline uint16_t
@@ -139,6 +149,233 @@ lpm_get_dst_port_with_ipv4(const struct lcore_conf *qconf, struct rte_mbuf *pkt,
 #include "l3fwd_lpm.h"
 #endif
 
+static int
+parse_ipv6_addr_mask(char *token, uint32_t *ipv6, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET6, sa, ipv6) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 128, 0);
+	return 0;
+}
+
+static int
+parse_ipv4_addr_mask(char *token, uint32_t *ipv4, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET, sa, ipv4) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 32, 0);
+	*ipv4 = ntohl(*ipv4);
+	return 0;
+}
+
+static int
+lpm_parse_v6_net(char *in, uint32_t *v, uint8_t *mask_len)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = parse_ipv6_addr_mask(in, v, mask_len);
+
+	return rc;
+}
+
+static int
+lpm_parse_v6_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], v->ip_32, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_parse_v4_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = parse_ipv4_addr_mask(in[CB_FLD_DST_ADDR], &v->ip, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_add_rules(const char *rule_path,
+		struct lpm_route_rule **proute_base,
+		int (*parser)(char *, struct lpm_route_rule *))
+{
+	struct lpm_route_rule *route_rules;
+	struct lpm_route_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
+static int
+lpm_add_default_v4_rules(void)
+{
+	/* populate the LPM IPv4 table */
+	unsigned int i, rule_size = sizeof(*route_base_v4);
+	route_num_v4 = RTE_DIM(ipv4_l3fwd_route_array);
+
+	route_base_v4 = calloc(route_num_v4, rule_size);
+
+	for (i = 0; i < (unsigned int)route_num_v4; i++) {
+		route_base_v4[i].ip = ipv4_l3fwd_route_array[i].ip;
+		route_base_v4[i].depth = ipv4_l3fwd_route_array[i].depth;
+		route_base_v4[i].if_out = ipv4_l3fwd_route_array[i].if_out;
+	}
+	return 0;
+}
+
+static int
+lpm_add_default_v6_rules(void)
+{
+	/* populate the LPM IPv6 table */
+	unsigned int i, rule_size = sizeof(*route_base_v6);
+	route_num_v6 = RTE_DIM(ipv6_l3fwd_route_array);
+
+	route_base_v6 = calloc(route_num_v6, rule_size);
+
+	for (i = 0; i < (unsigned int)route_num_v6; i++) {
+		*(route_base_v6[i].ip_8) = *(ipv6_l3fwd_route_array[i].ip);
+		route_base_v6[i].depth = ipv6_l3fwd_route_array[i].depth;
+		route_base_v6[i].if_out = ipv6_l3fwd_route_array[i].if_out;
+	}
+	return 0;
+}
+
+void
+lpm_free_routes(void)
+{
+	free(route_base_v4);
+	free(route_base_v6);
+	route_base_v4 = NULL;
+	route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
+}
+
 /* main processing loop */
 int
 lpm_main_loop(__rte_unused void *dummy)
@@ -548,13 +785,47 @@ lpm_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
+/* Load rules from the input file */
+void
+read_config_files_lpm(void)
+{
+	if (parm_config.rule_ipv4_name != NULL &&
+			parm_config.rule_ipv6_name != NULL) {
+		/* ipv4 check */
+		route_num_v4 = lpm_add_rules(parm_config.rule_ipv4_name,
+					&route_base_v4, &lpm_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			lpm_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
+		}
+
+		/* ipv6 check */
+		route_num_v6 = lpm_add_rules(parm_config.rule_ipv6_name,
+					&route_base_v6, &lpm_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			lpm_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(INFO, L3FWD, "Missing 1 or more rule files, using default instead\n");
+		if (lpm_add_default_v4_rules() < 0) {
+			lpm_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add default IPv4 rules\n");
+		}
+		if (lpm_add_default_v6_rules() < 0) {
+			lpm_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add default IPv6 rules\n");
+		}
+	}
+}
+
 void
 setup_lpm(const int socketid)
 {
 	struct rte_eth_dev_info dev_info;
 	struct rte_lpm6_config config;
 	struct rte_lpm_config config_ipv4;
-	unsigned i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -572,32 +843,33 @@ setup_lpm(const int socketid)
 			socketid);
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* skip unused ports */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			lpm_free_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
 		       inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
-		       ipv4_l3fwd_route_array[i].depth,
-		       ipv4_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v4[i].depth,
+		       route_base_v4[i].if_out, dev_info.device->name);
 	}
 
 	/* create the LPM6 table */
@@ -608,37 +880,40 @@ setup_lpm(const int socketid)
 	config.flags = 0;
 	ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid,
 				&config);
-	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL) {
+		lpm_free_routes();
 		rte_exit(EXIT_FAILURE,
 			"Unable to create the l3fwd LPM table on socket %d\n",
 			socketid);
+	}
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			lpm_free_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
-		       inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip, abuf,
+		       inet_ntop(AF_INET6, route_base_v6[i].ip_8, abuf,
 				 sizeof(abuf)),
-		       ipv6_l3fwd_route_array[i].depth,
-		       ipv6_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v6[i].depth,
+		       route_base_v6[i].if_out, dev_info.device->name);
 	}
 }
 
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index c7eba06c4d..843ffbe9fa 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -14,6 +14,23 @@ struct ipv6_l3fwd_route {
 	uint8_t if_out;
 };
 
+struct lpm_route_rule {
+	union {
+		uint32_t ip;
+		union {
+			uint32_t ip_32[IPV6_ADDR_U32];
+			uint8_t ip_8[IPV6_ADDR_LEN];
+		};
+	};
+	uint8_t depth;
+	uint8_t if_out;
+};
+
+extern struct lpm_route_rule *route_base_v4;
+extern struct lpm_route_rule *route_base_v6;
+extern int route_num_v4;
+extern int route_num_v6;
+
 extern const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[16];
 
 extern const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[16];
diff --git a/examples/l3fwd/lpm_default_v4.cfg b/examples/l3fwd/lpm_default_v4.cfg
new file mode 100644
index 0000000000..4db5597ed8
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD LPM
+R198.18.0.0/24 0
+R198.18.1.0/24 1
+R198.18.2.0/24 2
+R198.18.3.0/24 3
+R198.18.4.0/24 4
+R198.18.5.0/24 5
+R198.18.6.0/24 6
+R198.18.7.0/24 7
+R198.18.8.0/24 8
+R198.18.9.0/24 9
+R198.18.10.0/24 10
+R198.18.11.0/24 11
+R198.18.12.0/24 12
+R198.18.13.0/24 13
+R198.18.14.0/24 14
+R198.18.15.0/24 15
diff --git a/examples/l3fwd/lpm_default_v6.cfg b/examples/l3fwd/lpm_default_v6.cfg
new file mode 100644
index 0000000000..c50233e0ba
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD LPM
+R2001:0200:0000:0000:0000:0000:0000:0000/64 0
+R2001:0200:0000:0001:0000:0000:0000:0000/64 1
+R2001:0200:0000:0002:0000:0000:0000:0000/64 2
+R2001:0200:0000:0003:0000:0000:0000:0000/64 3
+R2001:0200:0000:0004:0000:0000:0000:0000/64 4
+R2001:0200:0000:0005:0000:0000:0000:0000/64 5
+R2001:0200:0000:0006:0000:0000:0000:0000/64 6
+R2001:0200:0000:0007:0000:0000:0000:0000/64 7
+R2001:0200:0000:0008:0000:0000:0000:0000/64 8
+R2001:0200:0000:0009:0000:0000:0000:0000/64 9
+R2001:0200:0000:000A:0000:0000:0000:0000/64 10
+R2001:0200:0000:000B:0000:0000:0000:0000/64 11
+R2001:0200:0000:000C:0000:0000:0000:0000/64 12
+R2001:0200:0000:000D:0000:0000:0000:0000/64 13
+R2001:0200:0000:000E:0000:0000:0000:0000/64 14
+R2001:0200:0000:000F:0000:0000:0000:0000/64 15
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index eb68ffc5aa..8383d8f095 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -94,6 +94,8 @@ uint32_t hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 
 struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+struct parm_cfg parm_config;
+
 struct lcore_params {
 	uint16_t port_id;
 	uint8_t queue_id;
@@ -141,41 +143,49 @@ static struct rte_mempool *vector_pool[RTE_MAX_ETHPORTS];
 static uint8_t lkp_per_socket[NB_SOCKETS];
 
 struct l3fwd_lkp_mode {
+	void  (*read_config_files)(void);
 	void  (*setup)(int);
 	int   (*check_ptype)(int);
 	rte_rx_callback_fn cb_parse_ptype;
 	int   (*main_loop)(void *);
 	void* (*get_ipv4_lookup_struct)(int);
 	void* (*get_ipv6_lookup_struct)(int);
+	void  (*free_routes)(void);
 };
 
 static struct l3fwd_lkp_mode l3fwd_lkp;
 
 static struct l3fwd_lkp_mode l3fwd_em_lkp = {
+	.read_config_files		= read_config_files_em,
 	.setup                  = setup_hash,
 	.check_ptype		= em_check_ptype,
 	.cb_parse_ptype		= em_cb_parse_ptype,
 	.main_loop              = em_main_loop,
 	.get_ipv4_lookup_struct = em_get_ipv4_l3fwd_lookup_struct,
 	.get_ipv6_lookup_struct = em_get_ipv6_l3fwd_lookup_struct,
+	.free_routes			= em_free_routes,
 };
 
 static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_lpm,
 	.check_ptype		= lpm_check_ptype,
 	.cb_parse_ptype		= lpm_cb_parse_ptype,
 	.main_loop              = lpm_main_loop,
 	.get_ipv4_lookup_struct = lpm_get_ipv4_l3fwd_lookup_struct,
 	.get_ipv6_lookup_struct = lpm_get_ipv6_l3fwd_lookup_struct,
+	.free_routes			= lpm_free_routes,
 };
 
 static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_fib,
 	.check_ptype            = lpm_check_ptype,
 	.cb_parse_ptype         = lpm_cb_parse_ptype,
 	.main_loop              = fib_main_loop,
 	.get_ipv4_lookup_struct = fib_get_ipv4_l3fwd_lookup_struct,
 	.get_ipv6_lookup_struct = fib_get_ipv6_l3fwd_lookup_struct,
+	.free_routes			= lpm_free_routes,
 };
 
 /*
@@ -224,6 +234,21 @@ const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[] = {
 	{{32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 15},
 };
 
+/*
+ * API's called during initialization to setup ACL/EM/LPM rules.
+ */
+static void
+l3fwd_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
+
+static void
+l3fwd_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
+
 /*
  * Setup lookup methods for forwarding.
  * Currently exact-match, longest-prefix-match and forwarding information
@@ -339,6 +364,8 @@ print_usage(const char *prgname)
 {
 	fprintf(stderr, "%s [EAL options] --"
 		" -p PORTMASK"
+		"  --rule_ipv4=FILE"
+		"  --rule_ipv6=FILE"
 		" [-P]"
 		" [--lookup]"
 		" --config (port,queue,lcore)[,(port,queue,lcore)]"
@@ -381,7 +408,10 @@ print_usage(const char *prgname)
 		"  --event-vector-size: Max vector size if event vectorization is enabled.\n"
 		"  --event-vector-tmo: Max timeout to form vector in nanoseconds if event vectorization is enabled\n"
 		"  -E : Enable exact match, legacy flag please use --lookup=em instead\n"
-		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n\n",
+		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n"
+		"  --rule_ipv4=FILE: Specify the ipv4 rules entries file.\n"
+		"                    Each rule occupies one line.\n"
+		"  --rule_ipv6=FILE: Specify the ipv6 rules entries file.\n\n",
 		prgname);
 }
 
@@ -596,6 +626,8 @@ static const char short_options[] =
 #define CMD_LINE_OPT_ENABLE_VECTOR "event-vector"
 #define CMD_LINE_OPT_VECTOR_SIZE "event-vector-size"
 #define CMD_LINE_OPT_VECTOR_TMO_NS "event-vector-tmo"
+#define CMD_LINE_OPT_RULE_IPV4 "rule_ipv4"
+#define CMD_LINE_OPT_RULE_IPV6 "rule_ipv6"
 
 enum {
 	/* long options mapped to a short option */
@@ -610,6 +642,8 @@ enum {
 	CMD_LINE_OPT_MAX_PKT_LEN_NUM,
 	CMD_LINE_OPT_HASH_ENTRY_NUM_NUM,
 	CMD_LINE_OPT_PARSE_PTYPE_NUM,
+	CMD_LINE_OPT_RULE_IPV4_NUM,
+	CMD_LINE_OPT_RULE_IPV6_NUM,
 	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
 	CMD_LINE_OPT_MODE_NUM,
 	CMD_LINE_OPT_EVENTQ_SYNC_NUM,
@@ -637,6 +671,8 @@ static const struct option lgopts[] = {
 	{CMD_LINE_OPT_ENABLE_VECTOR, 0, 0, CMD_LINE_OPT_ENABLE_VECTOR_NUM},
 	{CMD_LINE_OPT_VECTOR_SIZE, 1, 0, CMD_LINE_OPT_VECTOR_SIZE_NUM},
 	{CMD_LINE_OPT_VECTOR_TMO_NS, 1, 0, CMD_LINE_OPT_VECTOR_TMO_NS_NUM},
+	{CMD_LINE_OPT_RULE_IPV4,   1, 0, CMD_LINE_OPT_RULE_IPV4_NUM},
+	{CMD_LINE_OPT_RULE_IPV6,   1, 0, CMD_LINE_OPT_RULE_IPV6_NUM},
 	{NULL, 0, 0, 0}
 };
 
@@ -791,6 +827,12 @@ parse_args(int argc, char **argv)
 		case CMD_LINE_OPT_VECTOR_TMO_NS_NUM:
 			evt_rsrc->vector_tmo_ns = strtoull(optarg, NULL, 10);
 			break;
+		case CMD_LINE_OPT_RULE_IPV4_NUM:
+			l3fwd_set_rule_ipv4_name(optarg);
+			break;
+		case CMD_LINE_OPT_RULE_IPV6_NUM:
+			l3fwd_set_rule_ipv6_name(optarg);
+			break;
 		default:
 			print_usage(prgname);
 			return -1;
@@ -1358,6 +1400,24 @@ l3fwd_event_service_setup(void)
 	}
 }
 
+/* Bypass comment and empty lines */
+int
+is_bypass_line(const char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
 int
 main(int argc, char **argv)
 {
@@ -1395,6 +1455,9 @@ main(int argc, char **argv)
 	/* Setup function pointers for lookup method. */
 	setup_l3fwd_lookup_tables();
 
+	/* Add the config file rules */
+	l3fwd_lkp.read_config_files();
+
 	evt_rsrc->per_port_pool = per_port_pool;
 	evt_rsrc->pkt_pool = pktmbuf_pool;
 	evt_rsrc->vec_pool = vector_pool;
@@ -1501,6 +1564,9 @@ main(int argc, char **argv)
 		}
 	}
 
+	/* clean up config file routes */
+	l3fwd_lkp.free_routes();
+
 	/* clean up the EAL */
 	rte_eal_cleanup();
 
-- 
2.25.1


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

* [PATCH v5 2/2] examples/l3fwd: add config file support for EM
  2022-02-04 19:59       ` [PATCH v5 0/2] Add config file support for l3fwd Sean Morrissey
  2022-02-04 19:59         ` [PATCH v5 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
@ 2022-02-04 19:59         ` Sean Morrissey
  2022-02-04 22:26         ` [PATCH v5 0/2] Add config file support for l3fwd Stephen Hemminger
  2022-03-01 14:49         ` [PATCH v6 " Sean Morrissey
  3 siblings, 0 replies; 46+ messages in thread
From: Sean Morrissey @ 2022-02-04 19:59 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Konstantin Ananyev

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for EM with a format
similar to l3fwd-acl one.

Users can now use the default hardcoded route tables
or optionally config files for 'l3fwd_em'. Default
config files have been provided for use with EM.

Related l3fwd docs have been updated to relfect these
changes.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
 examples/l3fwd/em_default_v4.cfg        |  17 +
 examples/l3fwd/em_default_v6.cfg        |  17 +
 examples/l3fwd/l3fwd_em.c               | 468 +++++++++++++++++-------
 examples/l3fwd/l3fwd_route.h            |  24 ++
 5 files changed, 459 insertions(+), 156 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg

diff --git a/doc/guides/sample_app_ug/l3_forward.rst b/doc/guides/sample_app_ug/l3_forward.rst
index 6d7d7c5cc1..01d86db95d 100644
--- a/doc/guides/sample_app_ug/l3_forward.rst
+++ b/doc/guides/sample_app_ug/l3_forward.rst
@@ -47,6 +47,7 @@ and loaded into the LPM or FIB object at initialization time.
 In the sample application, hash-based and FIB-based forwarding supports
 both IPv4 and IPv6.
 LPM-based forwarding supports IPv4 only.
+During the initialization phase route rules for IPv4 and IPv6 are read from rule files.
 
 Compiling the Application
 -------------------------
@@ -61,6 +62,8 @@ Running the Application
 The application has a number of command line options::
 
     ./dpdk-l3fwd [EAL options] -- -p PORTMASK
+                             --rule_ipv4=FILE
+                             --rule_ipv6=FILE
                              [-P]
                              [--lookup LOOKUP_METHOD]
                              --config(port,queue,lcore)[,(port,queue,lcore)]
@@ -82,6 +85,11 @@ Where,
 
 * ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
 
+* ``--rule_ipv4=FILE:`` specify the ipv4 rules entries file.
+  Each rule occupies one line.
+
+* ``--rule_ipv6=FILE:`` specify the ipv6 rules entries file.
+
 * ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
   Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
 
@@ -135,7 +143,7 @@ To enable L3 forwarding between two ports, assuming that both ports are in the s
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
+    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)" --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -157,19 +165,23 @@ In this command:
 |          |           |           |                                     |
 +----------+-----------+-----------+-------------------------------------+
 
+*   The -rule_ipv4 option specifies the reading of IPv4 rules sets from the rule_ipv4.cfg file
+
+*   The -rule_ipv6 option specifies the reading of IPv6 rules sets from the rule_ipv6.cfg file.
+
 To use eventdev mode with sync method **ordered** on above mentioned environment,
 Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 or
 
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> \
-		-- -p 0x03 --mode=eventdev --eventq-sched=ordered
+		-- -p 0x03 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -192,7 +204,7 @@ scheduler. Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In case of eventdev mode, *--config* option is not used for ethernet port
 configuration. Instead each ethernet port will be configured with mentioned
@@ -216,6 +228,49 @@ The following sections provide some explanation of the sample application code.
 the initialization and run-time paths are very similar to those of the :doc:`l2_forward_real_virtual` and :doc:`l2_forward_event`.
 The following sections describe aspects that are specific to the L3 Forwarding sample application.
 
+Parse Rules from File
+~~~~~~~~~~~~~~~~~~~~~
+
+The application parses the rules from the file and adds them to the appropriate route table by calling the appropriate function.
+It ignores empty and comment lines, and parses and validates the rules it reads.
+If errors are detected, the application exits with messages to identify the errors encountered.
+
+The format of the route rules differs based on which lookup method is being used.
+Therefore, the code only decreases the priority number with each rule it parses.
+Route rules are mandatory.
+To read data from the specified file successfully, the application assumes the following:
+
+*   Each rule occupies a single line.
+
+*   Only the following four rule line types are valid in this application:
+
+*   Route rule line, which starts with a leading character 'R'
+
+*   Comment line, which starts with a leading character '#'
+
+*   Empty line, which consists of a space, form-feed ('\f'), newline ('\n'),
+    carriage return ('\r'), horizontal tab ('\t'), or vertical tab ('\v').
+
+Other lines types are considered invalid.
+
+*   Rules are organized in descending order of priority,
+    which means rules at the head of the file always have a higher priority than those further down in the file.
+
+*   A typical IPv4 LPM/FIB rule line should have a format as shown below:
+
+R<destination_ip>/<ip_mask_length><output_port_number>
+
+*   A typical IPv4 EM rule line should have a format as shown below:
+
+R<destination_ip><source_ip><destination_port><source_port><protocol><output_port_number>
+
+IPv4 addresses are specified in CIDR format as specified in RFC 4632.
+For LPM/FIB they consist of the dot notation for the address and a prefix length separated by '/'.
+For example, 192.168.0.34/32, where the address is 192.168.0.34 and the prefix length is 32.
+For EM they consist of just the dot notation for the address and no prefix length.
+For example, 192.168.0.34, where the Address is 192.168.0.34.
+EM also includes ports which are specified as a single number which represents a single port.
+
 Hash Initialization
 ~~~~~~~~~~~~~~~~~~~
 
@@ -227,8 +282,6 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
 
     The Hash initialization will setup both ipv4 and ipv6 hash table,
     and populate the either table depending on the value of variable ipv6.
-    To support the hash performance test with up to 8M single direction flows/16M bi-direction flows,
-    populate_ipv4_many_flow_into_table() function will populate the hash table with specified hash table entry number(default 4M).
 
 .. note::
 
@@ -246,22 +299,14 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
         {
             // ...
 
-            if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_many_flow_into_table(ipv4_l3fwd_lookup_struct[socketid], hash_entry_number);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_many_flow_into_table( ipv6_l3fwd_lookup_struct[socketid], hash_entry_number);
-                }
-            } else
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_few_flow_into_table(ipv4_l3fwd_lookup_struct[socketid]);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_few_flow_into_table(ipv6_l3fwd_lookup_struct[socketid]);
-                }
+            if (ipv6 == 0) {
+                /* populate the ipv4 hash */
+                populate_ipv4_flow_into_table(
+                    ipv4_l3fwd_em_lookup_struct[socketid]);
+            } else {
+                /* populate the ipv6 hash */
+                populate_ipv6_flow_into_table(
+                    ipv6_l3fwd_em_lookup_struct[socketid]);
             }
         }
     #endif
diff --git a/examples/l3fwd/em_default_v4.cfg b/examples/l3fwd/em_default_v4.cfg
new file mode 100644
index 0000000000..b70bdb6601
--- /dev/null
+++ b/examples/l3fwd/em_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD EM
+R198.18.0.0 198.18.0.1 9 9 0x11 0
+R198.18.1.0 198.18.1.1 9 9 0x11 1
+R198.18.2.0 198.18.2.1 9 9 0x11 2
+R198.18.3.0 198.18.3.1 9 9 0x11 3
+R198.18.4.0 198.18.4.1 9 9 0x11 4
+R198.18.5.0 198.18.5.1 9 9 0x11 5
+R198.18.6.0 198.18.6.1 9 9 0x11 6
+R198.18.7.0 198.18.7.1 9 9 0x11 7
+R198.18.8.0 198.18.8.1 9 9 0x11 8
+R198.18.9.0 198.18.9.1 9 9 0x11 9
+R198.18.10.0 198.18.10.1 9 9 0x11 10
+R198.18.11.0 198.18.11.1 9 9 0x11 11
+R198.18.12.0 198.18.12.1 9 9 0x11 12
+R198.18.13.0 198.18.13.1 9 9 0x11 13
+R198.18.14.0 198.18.14.1 9 9 0x11 14
+R198.18.15.0 198.18.15.1 9 9 0x11 15
diff --git a/examples/l3fwd/em_default_v6.cfg b/examples/l3fwd/em_default_v6.cfg
new file mode 100644
index 0000000000..0b94f8d8d2
--- /dev/null
+++ b/examples/l3fwd/em_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD EM
+R2001:0200:0000:0000:0000:0000:0000:0000 2001:0200:0000:0000:0000:0000:0000:0001 9 9 0x11 0
+R2001:0200:0000:0001:0000:0000:0000:0000 2001:0200:0000:0001:0000:0000:0000:0001 9 9 0x11 1
+R2001:0200:0000:0002:0000:0000:0000:0000 2001:0200:0000:0002:0000:0000:0000:0001 9 9 0x11 2
+R2001:0200:0000:0003:0000:0000:0000:0000 2001:0200:0000:0003:0000:0000:0000:0001 9 9 0x11 3
+R2001:0200:0000:0004:0000:0000:0000:0000 2001:0200:0000:0004:0000:0000:0000:0001 9 9 0x11 4
+R2001:0200:0000:0005:0000:0000:0000:0000 2001:0200:0000:0005:0000:0000:0000:0001 9 9 0x11 5
+R2001:0200:0000:0006:0000:0000:0000:0000 2001:0200:0000:0006:0000:0000:0000:0001 9 9 0x11 6
+R2001:0200:0000:0007:0000:0000:0000:0000 2001:0200:0000:0007:0000:0000:0000:0001 9 9 0x11 7
+R2001:0200:0000:0008:0000:0000:0000:0000 2001:0200:0000:0008:0000:0000:0000:0001 9 9 0x11 8
+R2001:0200:0000:0009:0000:0000:0000:0000 2001:0200:0000:0009:0000:0000:0000:0001 9 9 0x11 9
+R2001:0200:0000:000A:0000:0000:0000:0000 2001:0200:0000:000A:0000:0000:0000:0001 9 9 0x11 10
+R2001:0200:0000:000B:0000:0000:0000:0000 2001:0200:0000:000B:0000:0000:0000:0001 9 9 0x11 11
+R2001:0200:0000:000C:0000:0000:0000:0000 2001:0200:0000:000C:0000:0000:0000:0001 9 9 0x11 12
+R2001:0200:0000:000D:0000:0000:0000:0000 2001:0200:0000:000D:0000:0000:0000:0001 9 9 0x11 13
+R2001:0200:0000:000E:0000:0000:0000:0000 2001:0200:0000:000E:0000:0000:0000:0001 9 9 0x11 14
+R2001:0200:0000:000F:0000:0000:0000:0000 2001:0200:0000:000F:0000:0000:0000:0001 9 9 0x11 15
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 9058e75a76..dd1fc4a4be 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -27,6 +27,7 @@
 
 #include "l3fwd.h"
 #include "l3fwd_event.h"
+#include "l3fwd_route.h"
 
 #if defined(RTE_ARCH_X86) || defined(__ARM_FEATURE_CRC32)
 #define EM_HASH_CRC 1
@@ -42,13 +43,18 @@
 
 #define IPV6_ADDR_LEN 16
 
-struct ipv4_5tuple {
-	uint32_t ip_dst;
-	uint32_t ip_src;
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
+static struct em_rule *em_route_base_v4;
+static struct em_rule *em_route_base_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_SRC_ADDR,
+	CB_FLD_DST_PORT,
+	CB_FLD_SRC_PORT,
+	CB_FLD_PROTO,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
 
 union ipv4_5tuple_host {
 	struct {
@@ -65,14 +71,6 @@ union ipv4_5tuple_host {
 
 #define XMM_NUM_IN_IPV6_5TUPLE 3
 
-struct ipv6_5tuple {
-	uint8_t  ip_dst[IPV6_ADDR_LEN];
-	uint8_t  ip_src[IPV6_ADDR_LEN];
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
-
 union ipv6_5tuple_host {
 	struct {
 		uint16_t pad0;
@@ -87,8 +85,6 @@ union ipv6_5tuple_host {
 	xmm_t xmm[XMM_NUM_IN_IPV6_5TUPLE];
 };
 
-
-
 struct ipv4_l3fwd_em_route {
 	struct ipv4_5tuple key;
 	uint8_t if_out;
@@ -235,10 +231,6 @@ ipv6_hash_crc(const void *data, __rte_unused uint32_t data_len,
 	return init_val;
 }
 
-#define IPV4_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv4_l3fwd_em_route_array)
-
-#define IPV6_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv6_l3fwd_em_route_array)
-
 static uint8_t ipv4_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 static uint8_t ipv6_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 
@@ -346,6 +338,214 @@ em_get_ipv6_dst_port(void *ipv6_hdr, uint16_t portid, void *lookup_struct)
 #include "l3fwd_em.h"
 #endif
 
+static int
+em_parse_v6_net(const char *in, uint8_t *v)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = inet_pton(AF_INET6, in, v);
+	if (rc != 1)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+em_parse_v6_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], v->v6_key.ip_dst);
+	if (rc != 0)
+		return rc;
+	rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], v->v6_key.ip_src);
+	if (rc != 0)
+		return rc;
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_parse_v4_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = inet_pton(AF_INET, in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
+	v->v4_key.ip_dst = ntohl(v->v4_key.ip_dst);
+	if (rc != 1)
+		return rc;
+
+	rc = inet_pton(AF_INET, in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
+	v->v4_key.ip_src = ntohl(v->v4_key.ip_src);
+	if (rc != 1)
+		return rc;
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_add_rules(const char *rule_path,
+		struct em_rule **proute_base,
+		int (*parser)(char *, struct em_rule *))
+{
+	struct em_rule *route_rules;
+	struct em_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
+static int
+em_add_default_v4_rules(void)
+{
+	/* populate the LPM IPv4 table */
+	unsigned int i, rule_size = sizeof(*em_route_base_v4);
+	route_num_v4 = RTE_DIM(ipv4_l3fwd_em_route_array);
+
+	em_route_base_v4 = calloc(route_num_v4, rule_size);
+
+	for (i = 0; i < (unsigned int)route_num_v4; i++) {
+		em_route_base_v4[i].v4_key.ip_dst = ipv4_l3fwd_em_route_array[i].key.ip_dst;
+		em_route_base_v4[i].v4_key.ip_src = ipv4_l3fwd_em_route_array[i].key.ip_src;
+		em_route_base_v4[i].v4_key.port_dst = ipv4_l3fwd_em_route_array[i].key.port_dst;
+		em_route_base_v4[i].v4_key.port_src = ipv4_l3fwd_em_route_array[i].key.port_src;
+		em_route_base_v4[i].v4_key.proto = ipv4_l3fwd_em_route_array[i].key.proto;
+		em_route_base_v4[i].if_out = ipv4_l3fwd_em_route_array[i].if_out;
+	}
+	return 0;
+}
+
+static int
+em_add_default_v6_rules(void)
+{
+	/* populate the LPM IPv6 table */
+	unsigned int i, rule_size = sizeof(*em_route_base_v6);
+	route_num_v6 = RTE_DIM(ipv6_l3fwd_em_route_array);
+
+	em_route_base_v6 = calloc(route_num_v6, rule_size);
+
+	for (i = 0; i < (unsigned int)route_num_v6; i++) {
+		*em_route_base_v6[i].v6_key.ip_dst = *ipv6_l3fwd_em_route_array[i].key.ip_dst;
+		*em_route_base_v6[i].v6_key.ip_src = *ipv6_l3fwd_em_route_array[i].key.ip_src;
+		em_route_base_v6[i].v6_key.port_dst = ipv6_l3fwd_em_route_array[i].key.port_dst;
+		em_route_base_v6[i].v6_key.port_src = ipv6_l3fwd_em_route_array[i].key.port_src;
+		em_route_base_v6[i].v6_key.proto = ipv6_l3fwd_em_route_array[i].key.proto;
+		em_route_base_v6[i].if_out = ipv6_l3fwd_em_route_array[i].if_out;
+	}
+	return 0;
+}
+
 static void
 convert_ipv4_5tuple(struct ipv4_5tuple *key1,
 		union ipv4_5tuple_host *key2)
@@ -382,122 +582,106 @@ convert_ipv6_5tuple(struct ipv6_5tuple *key1,
 #define BIT_8_TO_15 0x0000ff00
 
 static inline void
-populate_ipv4_few_flow_into_table(const struct rte_hash *h)
+populate_ipv4_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
-	for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv4_l3fwd_em_route  entry;
+	for (i = 0; i < route_num_v4; i++) {
+		struct em_rule *entry;
 		union ipv4_5tuple_host newkey;
+		struct in_addr src;
+		struct in_addr dst;
+
+		if ((1 << em_route_base_v4[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
 
-		entry = ipv4_l3fwd_em_route_array[i];
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		entry = &em_route_base_v4[i];
+		convert_ipv4_5tuple(&(entry->v4_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv4_l3fwd_out_if[ret] = entry.if_out;
+		ipv4_l3fwd_out_if[ret] = entry->if_out;
+		ret = rte_eth_dev_info_get(em_route_base_v4[i].if_out,
+				     &dev_info);
+		if (ret != 0)
+			rte_exit(EXIT_FAILURE,
+				"Error during getting device (port %u) info: %s\n",
+				em_route_base_v4[i].if_out, strerror(-ret));
+
+		src.s_addr = htonl(em_route_base_v4[i].v4_key.ip_src);
+		dst.s_addr = htonl(em_route_base_v4[i].v4_key.ip_dst);
+		printf("EM: Adding route %s, %s, %d, %d, %d (%d) [%s]\n",
+			   inet_ntop(AF_INET, &dst, dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET, &src, srcbuf, sizeof(srcbuf)),
+			   em_route_base_v4[i].v4_key.port_dst,
+			   em_route_base_v4[i].v4_key.port_src,
+			   em_route_base_v4[i].v4_key.proto,
+		       em_route_base_v4[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 " keys\n",
-		(uint64_t)IPV4_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v4);
 }
 
 #define BIT_16_TO_23 0x00ff0000
 static inline void
-populate_ipv6_few_flow_into_table(const struct rte_hash *h)
+populate_ipv6_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
-	for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv6_l3fwd_em_route entry;
+	for (i = 0; i < route_num_v6; i++) {
+		struct em_rule *entry;
 		union ipv6_5tuple_host newkey;
 
-		entry = ipv6_l3fwd_em_route_array[i];
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v6[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v6[i];
+		convert_ipv6_5tuple(&(entry->v6_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv6_l3fwd_out_if[ret] = entry.if_out;
+		ipv6_l3fwd_out_if[ret] = entry->if_out;
+		ret = rte_eth_dev_info_get(em_route_base_v6[i].if_out,
+				     &dev_info);
+		if (ret != 0)
+			rte_exit(EXIT_FAILURE,
+				"Error during getting device (port %u) info: %s\n",
+				em_route_base_v6[i].if_out, strerror(-ret));
+
+		printf("EM: Adding route %s, %s, %d, %d, %d (%d) [%s]\n",
+			   inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip_dst,
+			   dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip_src,
+			   srcbuf, sizeof(srcbuf)),
+			   em_route_base_v6[i].v6_key.port_dst,
+			   em_route_base_v6[i].v6_key.port_src,
+			   em_route_base_v6[i].v6_key.proto,
+		       em_route_base_v6[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 "keys\n",
-		(uint64_t)IPV6_L3FWD_EM_NUM_ROUTES);
-}
-
-#define NUMBER_PORT_USED 16
-static inline void
-populate_ipv4_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv4_l3fwd_em_route entry;
-		union ipv4_5tuple_host newkey;
-
-		uint8_t a = (uint8_t)((port + 1) % BYTE_VALUE_MAX);
-
-		/* Create the ipv4 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv4_l3fwd_em_route_array[port];
-		entry.key.ip_dst = RTE_IPV4(198, 18, port, a);
-		convert_ipv4_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv4_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
-}
-
-static inline void
-populate_ipv6_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv6_l3fwd_em_route entry;
-		union ipv6_5tuple_host newkey;
-
-		/* Create the ipv6 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv6_l3fwd_em_route_array[port];
-		entry.key.ip_dst[15] = (port + 1) % BYTE_VALUE_MAX;
-		convert_ipv6_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv6_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
+		(uint64_t)route_num_v6);
 }
 
 /* Requirements:
@@ -972,17 +1156,50 @@ em_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy)
 	return 0;
 }
 
-/* Load rules from the input file */
 void
-read_config_files_em(void)
+em_free_routes(void)
 {
-	/* Empty till config file support added to EM */
+	free(em_route_base_v4);
+	free(em_route_base_v6);
+	em_route_base_v4 = NULL;
+	em_route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
 }
 
+/* Load rules from the input file */
 void
-em_free_routes(void)
+read_config_files_em(void)
 {
-	/* Empty till config file support added to EM */
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL &&
+			parm_config.rule_ipv6_name != NULL) {
+		/* ipv4 check */
+		route_num_v4 = em_add_rules(parm_config.rule_ipv4_name,
+					&em_route_base_v4, &em_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			em_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv4 rules\n");
+		}
+
+		/* ipv6 check */
+		route_num_v6 = em_add_rules(parm_config.rule_ipv6_name,
+					&em_route_base_v6, &em_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			em_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(INFO, L3FWD, "Missing 1 or more rule files, using default instead\n");
+		if (em_add_default_v4_rules() < 0) {
+			em_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add default IPv4 rules\n");
+		}
+		if (em_add_default_v6_rules() < 0) {
+			em_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add default IPv6 rules\n");
+		}
+	}
 }
 
 /* Initialize exact match (hash) parameters. 8< */
@@ -1029,35 +1246,18 @@ setup_hash(const int socketid)
 			"Unable to create the l3fwd hash on socket %d\n",
 			socketid);
 
-	if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-		/* For testing hash matching with a large number of flows we
-		 * generate millions of IP 5-tuples with an incremented dst
-		 * address to initialize the hash table. */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_many_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_many_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		}
+	/*
+	 * Use data from ipv4/ipv6 l3fwd config file
+	 * directly to initialize the hash table.
+	 */
+	if (ipv6 == 0) {
+		/* populate the ipv4 hash */
+		populate_ipv4_flow_into_table(
+			ipv4_l3fwd_em_lookup_struct[socketid]);
 	} else {
-		/*
-		 * Use data in ipv4/ipv6 l3fwd lookup table
-		 * directly to initialize the hash table.
-		 */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_few_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid]);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_few_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid]);
-		}
+		/* populate the ipv6 hash */
+		populate_ipv6_flow_into_table(
+			ipv6_l3fwd_em_lookup_struct[socketid]);
 	}
 }
 /* >8 End of initialization of hash parameters. */
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index 843ffbe9fa..f48f7e1954 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -14,6 +14,22 @@ struct ipv6_l3fwd_route {
 	uint8_t if_out;
 };
 
+struct ipv4_5tuple {
+	uint32_t ip_dst;
+	uint32_t ip_src;
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
+
+struct ipv6_5tuple {
+	uint8_t  ip_dst[IPV6_ADDR_LEN];
+	uint8_t  ip_src[IPV6_ADDR_LEN];
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
+
 struct lpm_route_rule {
 	union {
 		uint32_t ip;
@@ -26,6 +42,14 @@ struct lpm_route_rule {
 	uint8_t if_out;
 };
 
+struct em_rule {
+		union {
+		struct ipv4_5tuple v4_key;
+		struct ipv6_5tuple v6_key;
+	};
+	uint8_t if_out;
+};
+
 extern struct lpm_route_rule *route_base_v4;
 extern struct lpm_route_rule *route_base_v6;
 extern int route_num_v4;
-- 
2.25.1


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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-04 19:59       ` [PATCH v5 0/2] Add config file support for l3fwd Sean Morrissey
  2022-02-04 19:59         ` [PATCH v5 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
  2022-02-04 19:59         ` [PATCH v5 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
@ 2022-02-04 22:26         ` Stephen Hemminger
  2022-02-06 15:16           ` Ananyev, Konstantin
  2022-03-01 14:49         ` [PATCH v6 " Sean Morrissey
  3 siblings, 1 reply; 46+ messages in thread
From: Stephen Hemminger @ 2022-02-04 22:26 UTC (permalink / raw)
  To: Sean Morrissey; +Cc: dev

On Fri,  4 Feb 2022 19:59:03 +0000
Sean Morrissey <sean.morrissey@intel.com> wrote:

> This patchset introduces config file support for l3fwd
> and its lookup methods LPM, FIB, and EM, similar to
> that of l3fwd-acl. This allows for route rules to be
> defined in configuration files and edited there instead
> of in each of the lookup methods hardcoded route tables.
> 
> V4:
> * Fix nondeterministic bug of segfault on termination of
>   sample app.
> V5:
> * Reintroduce hardcoded tables as to not break dts and
>   allow for hardcoded tables to be used if no config
>   files presented.
> 
> Sean Morrissey (2):
>   examples/l3fwd: add config file support for LPM/FIB
>   examples/l3fwd: add config file support for EM
> 
>  doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
>  examples/l3fwd/em_default_v4.cfg        |  17 +
>  examples/l3fwd/em_default_v6.cfg        |  17 +
>  examples/l3fwd/l3fwd.h                  |  41 +++
>  examples/l3fwd/l3fwd_em.c               | 471 +++++++++++++++++-------
>  examples/l3fwd/l3fwd_fib.c              |  50 +--
>  examples/l3fwd/l3fwd_lpm.c              | 315 +++++++++++++++-
>  examples/l3fwd/l3fwd_route.h            |  41 +++
>  examples/l3fwd/lpm_default_v4.cfg       |  17 +
>  examples/l3fwd/lpm_default_v6.cfg       |  17 +
>  examples/l3fwd/main.c                   |  68 +++-
>  11 files changed, 949 insertions(+), 194 deletions(-)
>  create mode 100644 examples/l3fwd/em_default_v4.cfg
>  create mode 100644 examples/l3fwd/em_default_v6.cfg
>  create mode 100644 examples/l3fwd/lpm_default_v4.cfg
>  create mode 100644 examples/l3fwd/lpm_default_v6.cfg
> 

Why not use the DPDK cfgfile library and format?
It is model after standard INI format.


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

* RE: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-04 22:26         ` [PATCH v5 0/2] Add config file support for l3fwd Stephen Hemminger
@ 2022-02-06 15:16           ` Ananyev, Konstantin
  2022-02-08  3:04             ` Stephen Hemminger
  0 siblings, 1 reply; 46+ messages in thread
From: Ananyev, Konstantin @ 2022-02-06 15:16 UTC (permalink / raw)
  To: Stephen Hemminger, Morrissey, Sean; +Cc: dev


> > This patchset introduces config file support for l3fwd
> > and its lookup methods LPM, FIB, and EM, similar to
> > that of l3fwd-acl. This allows for route rules to be
> > defined in configuration files and edited there instead
> > of in each of the lookup methods hardcoded route tables.
> >
> > V4:
> > * Fix nondeterministic bug of segfault on termination of
> >   sample app.
> > V5:
> > * Reintroduce hardcoded tables as to not break dts and
> >   allow for hardcoded tables to be used if no config
> >   files presented.
> >
> > Sean Morrissey (2):
> >   examples/l3fwd: add config file support for LPM/FIB
> >   examples/l3fwd: add config file support for EM
> >
> >  doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
> >  examples/l3fwd/em_default_v4.cfg        |  17 +
> >  examples/l3fwd/em_default_v6.cfg        |  17 +
> >  examples/l3fwd/l3fwd.h                  |  41 +++
> >  examples/l3fwd/l3fwd_em.c               | 471 +++++++++++++++++-------
> >  examples/l3fwd/l3fwd_fib.c              |  50 +--
> >  examples/l3fwd/l3fwd_lpm.c              | 315 +++++++++++++++-
> >  examples/l3fwd/l3fwd_route.h            |  41 +++
> >  examples/l3fwd/lpm_default_v4.cfg       |  17 +
> >  examples/l3fwd/lpm_default_v6.cfg       |  17 +
> >  examples/l3fwd/main.c                   |  68 +++-
> >  11 files changed, 949 insertions(+), 194 deletions(-)
> >  create mode 100644 examples/l3fwd/em_default_v4.cfg
> >  create mode 100644 examples/l3fwd/em_default_v6.cfg
> >  create mode 100644 examples/l3fwd/lpm_default_v4.cfg
> >  create mode 100644 examples/l3fwd/lpm_default_v6.cfg
> >
> 
> Why not use the DPDK cfgfile library and format?
> It is model after standard INI format.

It is probably some sort of misunderstanding:
This patch doesn't add configuration file for some l3fwd run-time parameters
(number of ports/queues, queue/cpu mappings, etc.).
It allows user to specify he's own routing table instead of hard-coded ones.
For routing table .ini file format is not really suitable.
Instead we follow format similar to what is used in other DPDK apps
(l3fwd-acl, ipsec-secgw, test-acl, test-fib,  test-sad, etc.) for these purposes:
list of route entries, each entry occupies exactly one line.
As an example:
/examples/l3fwd/lpm_default_v4.cfg
#Copy of hard-coded IPv4 FWD table for L3FWD LPM
R198.18.0.0/24 0
R198.18.1.0/24 1
R198.18.2.0/24 2
R198.18.3.0/24 3
....
I suppose it is self-explanatory, intuitive and close enough 
to what user used for with unix-like route config tools.
Konstantin


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

* RE: [PATCH v5 1/2] examples/l3fwd: add config file support for LPM/FIB
  2022-02-04 19:59         ` [PATCH v5 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
@ 2022-02-08  2:21           ` Han, YingyaX
  0 siblings, 0 replies; 46+ messages in thread
From: Han, YingyaX @ 2022-02-08  2:21 UTC (permalink / raw)
  To: Morrissey, Sean; +Cc: dev, Morrissey, Sean, Ananyev, Konstantin



-----Original Message-----
From: Sean Morrissey <sean.morrissey@intel.com> 
Sent: Saturday, February 5, 2022 3:59 AM
Cc: dev@dpdk.org; Morrissey, Sean <sean.morrissey@intel.com>; Ananyev, Konstantin <konstantin.ananyev@intel.com>
Subject: [PATCH v5 1/2] examples/l3fwd: add config file support for LPM/FIB

Add support to define ipv4 and ipv6 forwarding tables from reading from a config file for LPM and FIB, with format similar to l3fwd-acl one.

Users can now use the default hardcoded route tables or optionally config files. Default config files have been provided for use with LPM and FIB.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

Tested-by: Yingya Han < yingyax.han@intel.com>


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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-06 15:16           ` Ananyev, Konstantin
@ 2022-02-08  3:04             ` Stephen Hemminger
  2022-02-08 10:44               ` Ananyev, Konstantin
  0 siblings, 1 reply; 46+ messages in thread
From: Stephen Hemminger @ 2022-02-08  3:04 UTC (permalink / raw)
  To: Ananyev, Konstantin; +Cc: Morrissey, Sean, dev

On Sun, 6 Feb 2022 15:16:22 +0000
"Ananyev, Konstantin" <konstantin.ananyev@intel.com> wrote:

> > > This patchset introduces config file support for l3fwd
> > > and its lookup methods LPM, FIB, and EM, similar to
> > > that of l3fwd-acl. This allows for route rules to be
> > > defined in configuration files and edited there instead
> > > of in each of the lookup methods hardcoded route tables.
> > >
> > > V4:
> > > * Fix nondeterministic bug of segfault on termination of
> > >   sample app.
> > > V5:
> > > * Reintroduce hardcoded tables as to not break dts and
> > >   allow for hardcoded tables to be used if no config
> > >   files presented.
> > >
> > > Sean Morrissey (2):
> > >   examples/l3fwd: add config file support for LPM/FIB
> > >   examples/l3fwd: add config file support for EM
> > >
> > >  doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
> > >  examples/l3fwd/em_default_v4.cfg        |  17 +
> > >  examples/l3fwd/em_default_v6.cfg        |  17 +
> > >  examples/l3fwd/l3fwd.h                  |  41 +++
> > >  examples/l3fwd/l3fwd_em.c               | 471 +++++++++++++++++-------
> > >  examples/l3fwd/l3fwd_fib.c              |  50 +--
> > >  examples/l3fwd/l3fwd_lpm.c              | 315 +++++++++++++++-
> > >  examples/l3fwd/l3fwd_route.h            |  41 +++
> > >  examples/l3fwd/lpm_default_v4.cfg       |  17 +
> > >  examples/l3fwd/lpm_default_v6.cfg       |  17 +
> > >  examples/l3fwd/main.c                   |  68 +++-
> > >  11 files changed, 949 insertions(+), 194 deletions(-)
> > >  create mode 100644 examples/l3fwd/em_default_v4.cfg
> > >  create mode 100644 examples/l3fwd/em_default_v6.cfg
> > >  create mode 100644 examples/l3fwd/lpm_default_v4.cfg
> > >  create mode 100644 examples/l3fwd/lpm_default_v6.cfg
> > >  
> > 
> > Why not use the DPDK cfgfile library and format?
> > It is model after standard INI format.  
> 
> It is probably some sort of misunderstanding:
> This patch doesn't add configuration file for some l3fwd run-time parameters
> (number of ports/queues, queue/cpu mappings, etc.).
> It allows user to specify he's own routing table instead of hard-coded ones.
> For routing table .ini file format is not really suitable.
> Instead we follow format similar to what is used in other DPDK apps
> (l3fwd-acl, ipsec-secgw, test-acl, test-fib,  test-sad, etc.) for these purposes:
> list of route entries, each entry occupies exactly one line.
> As an example:
> /examples/l3fwd/lpm_default_v4.cfg
> #Copy of hard-coded IPv4 FWD table for L3FWD LPM
> R198.18.0.0/24 0
> R198.18.1.0/24 1
> R198.18.2.0/24 2
> R198.18.3.0/24 3
> ....
> I suppose it is self-explanatory, intuitive and close enough 
> to what user used for with unix-like route config tools.
> Konstantin

I was think either, use existing cfgfile and a a section of LPM
so that it could be an example and also have some generic code for handling
prefix entries.

Or have a generic library for reading LPM entries.  L3fwd is supposed
to be as small as possible (it no longer is), and the real work should
be done by libraries to make it easier to build other applications.


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

* RE: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-08  3:04             ` Stephen Hemminger
@ 2022-02-08 10:44               ` Ananyev, Konstantin
  2022-02-08 16:15                 ` Medvedkin, Vladimir
  0 siblings, 1 reply; 46+ messages in thread
From: Ananyev, Konstantin @ 2022-02-08 10:44 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Morrissey, Sean, dev, Medvedkin, Vladimir, Richardson, Bruce


> > > > This patchset introduces config file support for l3fwd
> > > > and its lookup methods LPM, FIB, and EM, similar to
> > > > that of l3fwd-acl. This allows for route rules to be
> > > > defined in configuration files and edited there instead
> > > > of in each of the lookup methods hardcoded route tables.
> > > >
> > > > V4:
> > > > * Fix nondeterministic bug of segfault on termination of
> > > >   sample app.
> > > > V5:
> > > > * Reintroduce hardcoded tables as to not break dts and
> > > >   allow for hardcoded tables to be used if no config
> > > >   files presented.
> > > >
> > > > Sean Morrissey (2):
> > > >   examples/l3fwd: add config file support for LPM/FIB
> > > >   examples/l3fwd: add config file support for EM
> > > >
> > > >  doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
> > > >  examples/l3fwd/em_default_v4.cfg        |  17 +
> > > >  examples/l3fwd/em_default_v6.cfg        |  17 +
> > > >  examples/l3fwd/l3fwd.h                  |  41 +++
> > > >  examples/l3fwd/l3fwd_em.c               | 471 +++++++++++++++++-------
> > > >  examples/l3fwd/l3fwd_fib.c              |  50 +--
> > > >  examples/l3fwd/l3fwd_lpm.c              | 315 +++++++++++++++-
> > > >  examples/l3fwd/l3fwd_route.h            |  41 +++
> > > >  examples/l3fwd/lpm_default_v4.cfg       |  17 +
> > > >  examples/l3fwd/lpm_default_v6.cfg       |  17 +
> > > >  examples/l3fwd/main.c                   |  68 +++-
> > > >  11 files changed, 949 insertions(+), 194 deletions(-)
> > > >  create mode 100644 examples/l3fwd/em_default_v4.cfg
> > > >  create mode 100644 examples/l3fwd/em_default_v6.cfg
> > > >  create mode 100644 examples/l3fwd/lpm_default_v4.cfg
> > > >  create mode 100644 examples/l3fwd/lpm_default_v6.cfg
> > > >
> > >
> > > Why not use the DPDK cfgfile library and format?
> > > It is model after standard INI format.
> >
> > It is probably some sort of misunderstanding:
> > This patch doesn't add configuration file for some l3fwd run-time parameters
> > (number of ports/queues, queue/cpu mappings, etc.).
> > It allows user to specify he's own routing table instead of hard-coded ones.
> > For routing table .ini file format is not really suitable.
> > Instead we follow format similar to what is used in other DPDK apps
> > (l3fwd-acl, ipsec-secgw, test-acl, test-fib,  test-sad, etc.) for these purposes:
> > list of route entries, each entry occupies exactly one line.
> > As an example:
> > /examples/l3fwd/lpm_default_v4.cfg
> > #Copy of hard-coded IPv4 FWD table for L3FWD LPM
> > R198.18.0.0/24 0
> > R198.18.1.0/24 1
> > R198.18.2.0/24 2
> > R198.18.3.0/24 3
> > ....
> > I suppose it is self-explanatory, intuitive and close enough
> > to what user used for with unix-like route config tools.
> > Konstantin
> 
> I was think either, use existing cfgfile and a a section of LPM

LPM can have thousands or even millions entries,
it probably wouldn't be nice to pollute all of them in .ini file
together with usual settings.
At least my preference would be to have them in separate file - clean and simple

> so that it could be an example and also have some generic code for handling
> prefix entries.

Didn't get your sentence above about 'generic code for handling prefix entries'.
Could you possibly elaborate.
 
> Or have a generic library for reading LPM entries.  L3fwd is supposed
> to be as small as possible (it no longer is), and the real work should
> be done by libraries to make it easier to build other applications.

I never heard users ask about such thing,
but if there is a demand for that, then I suppose it could be considered.
CC-ing LPM/FIB maintainers to comment.
Though I believe it should be a subject of separate patch and discussion 
(I think many questions will arise - what format should be, how to support 
different types of user-data, to make it generic enough, etc.).
Konstantin 


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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-08 10:44               ` Ananyev, Konstantin
@ 2022-02-08 16:15                 ` Medvedkin, Vladimir
  2022-02-08 17:49                   ` Stephen Hemminger
  0 siblings, 1 reply; 46+ messages in thread
From: Medvedkin, Vladimir @ 2022-02-08 16:15 UTC (permalink / raw)
  To: Ananyev, Konstantin, Stephen Hemminger
  Cc: Morrissey, Sean, dev, Richardson, Bruce

Hi all,

On 08/02/2022 10:44, Ananyev, Konstantin wrote:
> 
>>>>> This patchset introduces config file support for l3fwd
>>>>> and its lookup methods LPM, FIB, and EM, similar to
>>>>> that of l3fwd-acl. This allows for route rules to be
>>>>> defined in configuration files and edited there instead
>>>>> of in each of the lookup methods hardcoded route tables.
>>>>>
>>>>> V4:
>>>>> * Fix nondeterministic bug of segfault on termination of
>>>>>    sample app.
>>>>> V5:
>>>>> * Reintroduce hardcoded tables as to not break dts and
>>>>>    allow for hardcoded tables to be used if no config
>>>>>    files presented.
>>>>>
>>>>> Sean Morrissey (2):
>>>>>    examples/l3fwd: add config file support for LPM/FIB
>>>>>    examples/l3fwd: add config file support for EM
>>>>>
>>>>>   doc/guides/sample_app_ug/l3_forward.rst |  89 +++--
>>>>>   examples/l3fwd/em_default_v4.cfg        |  17 +
>>>>>   examples/l3fwd/em_default_v6.cfg        |  17 +
>>>>>   examples/l3fwd/l3fwd.h                  |  41 +++
>>>>>   examples/l3fwd/l3fwd_em.c               | 471 +++++++++++++++++-------
>>>>>   examples/l3fwd/l3fwd_fib.c              |  50 +--
>>>>>   examples/l3fwd/l3fwd_lpm.c              | 315 +++++++++++++++-
>>>>>   examples/l3fwd/l3fwd_route.h            |  41 +++
>>>>>   examples/l3fwd/lpm_default_v4.cfg       |  17 +
>>>>>   examples/l3fwd/lpm_default_v6.cfg       |  17 +
>>>>>   examples/l3fwd/main.c                   |  68 +++-
>>>>>   11 files changed, 949 insertions(+), 194 deletions(-)
>>>>>   create mode 100644 examples/l3fwd/em_default_v4.cfg
>>>>>   create mode 100644 examples/l3fwd/em_default_v6.cfg
>>>>>   create mode 100644 examples/l3fwd/lpm_default_v4.cfg
>>>>>   create mode 100644 examples/l3fwd/lpm_default_v6.cfg
>>>>>
>>>>
>>>> Why not use the DPDK cfgfile library and format?
>>>> It is model after standard INI format.
>>>
>>> It is probably some sort of misunderstanding:
>>> This patch doesn't add configuration file for some l3fwd run-time parameters
>>> (number of ports/queues, queue/cpu mappings, etc.).
>>> It allows user to specify he's own routing table instead of hard-coded ones.
>>> For routing table .ini file format is not really suitable.
>>> Instead we follow format similar to what is used in other DPDK apps
>>> (l3fwd-acl, ipsec-secgw, test-acl, test-fib,  test-sad, etc.) for these purposes:
>>> list of route entries, each entry occupies exactly one line.
>>> As an example:
>>> /examples/l3fwd/lpm_default_v4.cfg
>>> #Copy of hard-coded IPv4 FWD table for L3FWD LPM
>>> R198.18.0.0/24 0
>>> R198.18.1.0/24 1
>>> R198.18.2.0/24 2
>>> R198.18.3.0/24 3
>>> ....
>>> I suppose it is self-explanatory, intuitive and close enough
>>> to what user used for with unix-like route config tools.
>>> Konstantin
>>
>> I was think either, use existing cfgfile and a a section of LPM
> 
> LPM can have thousands or even millions entries,
> it probably wouldn't be nice to pollute all of them in .ini file
> together with usual settings.
> At least my preference would be to have them in separate file - clean and simple
> 

Agree with Konstantin

>> so that it could be an example and also have some generic code for handling
>> prefix entries.
> 
> Didn't get your sentence above about 'generic code for handling prefix entries'.
> Could you possibly elaborate.
> 

If it is about parsing prefix string, then it is probably possible to 
use cmdline_parse_ipaddr()?

>> Or have a generic library for reading LPM entries.  L3fwd is supposed
>> to be as small as possible (it no longer is), and the real work should
>> be done by libraries to make it easier to build other applications.
> 
> I never heard users ask about such thing,
> but if there is a demand for that, then I suppose it could be considered.
> CC-ing LPM/FIB maintainers to comment.
> Though I believe it should be a subject of separate patch and discussion
> (I think many questions will arise - what format should be, how to support
> different types of user-data, to make it generic enough, etc.).

Agree, it is very application specific, so it could be really difficult 
to make it generic.

> Konstantin
> 

-- 
Regards,
Vladimir

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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-08 16:15                 ` Medvedkin, Vladimir
@ 2022-02-08 17:49                   ` Stephen Hemminger
  2022-02-08 18:10                     ` Bruce Richardson
  2022-02-09 12:00                     ` Ananyev, Konstantin
  0 siblings, 2 replies; 46+ messages in thread
From: Stephen Hemminger @ 2022-02-08 17:49 UTC (permalink / raw)
  To: Medvedkin, Vladimir
  Cc: Ananyev, Konstantin, Morrissey, Sean, dev, Richardson, Bruce

On Tue, 8 Feb 2022 16:15:15 +0000
"Medvedkin, Vladimir" <vladimir.medvedkin@intel.com> wrote:

> >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> >> to be as small as possible (it no longer is), and the real work should
> >> be done by libraries to make it easier to build other applications.  
> > 
> > I never heard users ask about such thing,
> > but if there is a demand for that, then I suppose it could be considered.
> > CC-ing LPM/FIB maintainers to comment.
> > Though I believe it should be a subject of separate patch and discussion
> > (I think many questions will arise - what format should be, how to support
> > different types of user-data, to make it generic enough, etc.).  
> 
> Agree, it is very application specific, so it could be really difficult 
> to make it generic.

But several other also have LPM tables, so why not have common code for other applications.

examples/l3fwd-power/main.c
examples/ipsec-secgw/rt.c
examples/ip_fragmentation/main.c
examples/l3fwd/l3fwd_lpm.c
examples/ip_reassembly/main.c

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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-08 17:49                   ` Stephen Hemminger
@ 2022-02-08 18:10                     ` Bruce Richardson
  2022-02-09 12:00                     ` Ananyev, Konstantin
  1 sibling, 0 replies; 46+ messages in thread
From: Bruce Richardson @ 2022-02-08 18:10 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Medvedkin, Vladimir, Ananyev, Konstantin, Morrissey, Sean, dev

On Tue, Feb 08, 2022 at 09:49:04AM -0800, Stephen Hemminger wrote:
> On Tue, 8 Feb 2022 16:15:15 +0000
> "Medvedkin, Vladimir" <vladimir.medvedkin@intel.com> wrote:
> 
> > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > >> to be as small as possible (it no longer is), and the real work should
> > >> be done by libraries to make it easier to build other applications.  
> > > 
> > > I never heard users ask about such thing,
> > > but if there is a demand for that, then I suppose it could be considered.
> > > CC-ing LPM/FIB maintainers to comment.
> > > Though I believe it should be a subject of separate patch and discussion
> > > (I think many questions will arise - what format should be, how to support
> > > different types of user-data, to make it generic enough, etc.).  
> > 
> > Agree, it is very application specific, so it could be really difficult 
> > to make it generic.
> 
> But several other also have LPM tables, so why not have common code for other applications.
> 
> examples/l3fwd-power/main.c
> examples/ipsec-secgw/rt.c
> examples/ip_fragmentation/main.c
> examples/l3fwd/l3fwd_lpm.c
> examples/ip_reassembly/main.c

Could we perhaps add a "load" function to the lpm library itself?

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

* RE: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-08 17:49                   ` Stephen Hemminger
  2022-02-08 18:10                     ` Bruce Richardson
@ 2022-02-09 12:00                     ` Ananyev, Konstantin
  2022-02-09 13:54                       ` Bruce Richardson
  2022-02-22  9:59                       ` Thomas Monjalon
  1 sibling, 2 replies; 46+ messages in thread
From: Ananyev, Konstantin @ 2022-02-09 12:00 UTC (permalink / raw)
  To: Stephen Hemminger, Medvedkin, Vladimir
  Cc: Morrissey, Sean, dev, Richardson, Bruce



> > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > >> to be as small as possible (it no longer is), and the real work should
> > >> be done by libraries to make it easier to build other applications.
> > >
> > > I never heard users ask about such thing,
> > > but if there is a demand for that, then I suppose it could be considered.
> > > CC-ing LPM/FIB maintainers to comment.
> > > Though I believe it should be a subject of separate patch and discussion
> > > (I think many questions will arise - what format should be, how to support
> > > different types of user-data, to make it generic enough, etc.).
> >
> > Agree, it is very application specific, so it could be really difficult
> > to make it generic.
> 
> But several other also have LPM tables, so why not have common code for other applications.
> 
> examples/l3fwd-power/main.c
> examples/ipsec-secgw/rt.c
> examples/ip_fragmentation/main.c
> examples/l3fwd/l3fwd_lpm.c
> examples/ip_reassembly/main.c

Ah yes, that's good point.
All these examples (except ipsec-secgw) started as l3fwd clones,
so all of them have hard-coded LPM (and EM) tables too.
Yes it would be good thing to address that problem too,
and have some common code (and common routes file format) for all of them.
I don't know is that a good idea to introduce parse file function in LPM/FIB library
itself, might be better to  have something like examples/common/lpm_parse*.
Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
My suggestion would be for 22.03 go ahead with current l3fwd patches,
then later we can consider to make it common and update other examples.
Konstantin 

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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-09 12:00                     ` Ananyev, Konstantin
@ 2022-02-09 13:54                       ` Bruce Richardson
  2022-02-09 16:00                         ` Medvedkin, Vladimir
  2022-02-22  9:59                       ` Thomas Monjalon
  1 sibling, 1 reply; 46+ messages in thread
From: Bruce Richardson @ 2022-02-09 13:54 UTC (permalink / raw)
  To: Ananyev, Konstantin
  Cc: Stephen Hemminger, Medvedkin, Vladimir, Morrissey, Sean, dev

On Wed, Feb 09, 2022 at 12:00:40PM +0000, Ananyev, Konstantin wrote:
> 
> 
> > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > >> to be as small as possible (it no longer is), and the real work should
> > > >> be done by libraries to make it easier to build other applications.
> > > >
> > > > I never heard users ask about such thing,
> > > > but if there is a demand for that, then I suppose it could be considered.
> > > > CC-ing LPM/FIB maintainers to comment.
> > > > Though I believe it should be a subject of separate patch and discussion
> > > > (I think many questions will arise - what format should be, how to support
> > > > different types of user-data, to make it generic enough, etc.).
> > >
> > > Agree, it is very application specific, so it could be really difficult
> > > to make it generic.
> >
> > But several other also have LPM tables, so why not have common code for other applications.
> >
> > examples/l3fwd-power/main.c
> > examples/ipsec-secgw/rt.c
> > examples/ip_fragmentation/main.c
> > examples/l3fwd/l3fwd_lpm.c
> > examples/ip_reassembly/main.c
> 
> Ah yes, that's good point.
> All these examples (except ipsec-secgw) started as l3fwd clones,
> so all of them have hard-coded LPM (and EM) tables too.
> Yes it would be good thing to address that problem too,
> and have some common code (and common routes file format) for all of them.
> I don't know is that a good idea to introduce parse file function in LPM/FIB library
> itself, might be better to  have something like examples/common/lpm_parse*.

I think it really depends on whether you are planning on implementing a
general config file for the application, or whether the file(s) will only
contain the LPM/FIB routing entries. If the plan is reading just the
routing entries from file, then I definitely think that it might be
something that would be generally useful inside the library itself.

If it's a more general config file with other app settings in it, then an
examples-only parse function/file would make more sense.

> Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> My suggestion would be for 22.03 go ahead with current l3fwd patches,
> then later we can consider to make it common and update other examples.
> Konstantin

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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-09 13:54                       ` Bruce Richardson
@ 2022-02-09 16:00                         ` Medvedkin, Vladimir
  0 siblings, 0 replies; 46+ messages in thread
From: Medvedkin, Vladimir @ 2022-02-09 16:00 UTC (permalink / raw)
  To: Bruce Richardson, Ananyev, Konstantin
  Cc: Stephen Hemminger, Morrissey, Sean, dev

Hi,

On 09/02/2022 13:54, Bruce Richardson wrote:
> On Wed, Feb 09, 2022 at 12:00:40PM +0000, Ananyev, Konstantin wrote:
>>
>>
>>>>>> Or have a generic library for reading LPM entries.  L3fwd is supposed
>>>>>> to be as small as possible (it no longer is), and the real work should
>>>>>> be done by libraries to make it easier to build other applications.
>>>>>
>>>>> I never heard users ask about such thing,
>>>>> but if there is a demand for that, then I suppose it could be considered.
>>>>> CC-ing LPM/FIB maintainers to comment.
>>>>> Though I believe it should be a subject of separate patch and discussion
>>>>> (I think many questions will arise - what format should be, how to support
>>>>> different types of user-data, to make it generic enough, etc.).
>>>>
>>>> Agree, it is very application specific, so it could be really difficult
>>>> to make it generic.
>>>
>>> But several other also have LPM tables, so why not have common code for other applications.
>>>
>>> examples/l3fwd-power/main.c
>>> examples/ipsec-secgw/rt.c
>>> examples/ip_fragmentation/main.c
>>> examples/l3fwd/l3fwd_lpm.c
>>> examples/ip_reassembly/main.c
>>
>> Ah yes, that's good point.
>> All these examples (except ipsec-secgw) started as l3fwd clones,
>> so all of them have hard-coded LPM (and EM) tables too.
>> Yes it would be good thing to address that problem too,
>> and have some common code (and common routes file format) for all of them.
>> I don't know is that a good idea to introduce parse file function in LPM/FIB library
>> itself, might be better to  have something like examples/common/lpm_parse*.
> 
> I think it really depends on whether you are planning on implementing a
> general config file for the application, or whether the file(s) will only
> contain the LPM/FIB routing entries. If the plan is reading just the
> routing entries from file, then I definitely think that it might be
> something that would be generally useful inside the library itself.
> 
> If it's a more general config file with other app settings in it, then an
> examples-only parse function/file would make more sense.
> 

I vote for this code to be in examples where we can control the file 
format with LPM/FIB entries.
I don't think it's a good idea to move this API to the data plane 
library. I think it will only be used in our examples because dpdk users 
will have their own formats for routing information.

>> Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
>> My suggestion would be for 22.03 go ahead with current l3fwd patches,
>> then later we can consider to make it common and update other examples.
>> Konstantin

-- 
Regards,
Vladimir

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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-09 12:00                     ` Ananyev, Konstantin
  2022-02-09 13:54                       ` Bruce Richardson
@ 2022-02-22  9:59                       ` Thomas Monjalon
  2022-02-22 10:39                         ` Ananyev, Konstantin
  1 sibling, 1 reply; 46+ messages in thread
From: Thomas Monjalon @ 2022-02-22  9:59 UTC (permalink / raw)
  To: Stephen Hemminger, Medvedkin, Vladimir, dev, Morrissey, Sean,
	Richardson, Bruce, Ananyev, Konstantin
  Cc: dev

09/02/2022 13:00, Ananyev, Konstantin:
> 
> > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > >> to be as small as possible (it no longer is), and the real work should
> > > >> be done by libraries to make it easier to build other applications.
> > > >
> > > > I never heard users ask about such thing,
> > > > but if there is a demand for that, then I suppose it could be considered.
> > > > CC-ing LPM/FIB maintainers to comment.
> > > > Though I believe it should be a subject of separate patch and discussion
> > > > (I think many questions will arise - what format should be, how to support
> > > > different types of user-data, to make it generic enough, etc.).
> > >
> > > Agree, it is very application specific, so it could be really difficult
> > > to make it generic.
> > 
> > But several other also have LPM tables, so why not have common code for other applications.
> > 
> > examples/l3fwd-power/main.c
> > examples/ipsec-secgw/rt.c
> > examples/ip_fragmentation/main.c
> > examples/l3fwd/l3fwd_lpm.c
> > examples/ip_reassembly/main.c
> 
> Ah yes, that's good point.
> All these examples (except ipsec-secgw) started as l3fwd clones,
> so all of them have hard-coded LPM (and EM) tables too.
> Yes it would be good thing to address that problem too,
> and have some common code (and common routes file format) for all of them.
> I don't know is that a good idea to introduce parse file function in LPM/FIB library
> itself, might be better to  have something like examples/common/lpm_parse*.
> Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> My suggestion would be for 22.03 go ahead with current l3fwd patches,
> then later we can consider to make it common and update other examples.

I don't think this patch is urgent.
I suggest taking time to have common code for all examples
and target a merge in DPDK 22.07.



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

* RE: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-22  9:59                       ` Thomas Monjalon
@ 2022-02-22 10:39                         ` Ananyev, Konstantin
  2022-02-22 13:46                           ` Thomas Monjalon
  0 siblings, 1 reply; 46+ messages in thread
From: Ananyev, Konstantin @ 2022-02-22 10:39 UTC (permalink / raw)
  To: Thomas Monjalon, Stephen Hemminger, Medvedkin, Vladimir, dev,
	Morrissey, Sean, Richardson, Bruce
  Cc: dev


> > > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > > >> to be as small as possible (it no longer is), and the real work should
> > > > >> be done by libraries to make it easier to build other applications.
> > > > >
> > > > > I never heard users ask about such thing,
> > > > > but if there is a demand for that, then I suppose it could be considered.
> > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > Though I believe it should be a subject of separate patch and discussion
> > > > > (I think many questions will arise - what format should be, how to support
> > > > > different types of user-data, to make it generic enough, etc.).
> > > >
> > > > Agree, it is very application specific, so it could be really difficult
> > > > to make it generic.
> > >
> > > But several other also have LPM tables, so why not have common code for other applications.
> > >
> > > examples/l3fwd-power/main.c
> > > examples/ipsec-secgw/rt.c
> > > examples/ip_fragmentation/main.c
> > > examples/l3fwd/l3fwd_lpm.c
> > > examples/ip_reassembly/main.c
> >
> > Ah yes, that's good point.
> > All these examples (except ipsec-secgw) started as l3fwd clones,
> > so all of them have hard-coded LPM (and EM) tables too.
> > Yes it would be good thing to address that problem too,
> > and have some common code (and common routes file format) for all of them.
> > I don't know is that a good idea to introduce parse file function in LPM/FIB library
> > itself, might be better to  have something like examples/common/lpm_parse*.
> > Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> > My suggestion would be for 22.03 go ahead with current l3fwd patches,
> > then later we can consider to make it common and update other examples.
> 
> I don't think this patch is urgent.
> I suggest taking time to have common code for all examples
> and target a merge in DPDK 22.07.

Well, yes, from one perspective it not really a critical one,
we do live with hard-coded routes inside l3fwd for nearly 10 year by now.
Though l3fwd is one of mostly used examples inside DPDK and
it is quite a pain to patch/rebuild it each time someone needs to run
l3fwd with a different routing table. 
Merging this patch will allow people to use l3fwd for more realistic test
scenarios in a painless manner.
So I believe this patch is really helpful and should be beneficial for the whole community.
Looking from that perspective, I don't see why it has to be "all or nothing" attitude here.
Why we can't move one step at a time instead?
That would allow to split and effort in terms of development/testing/upstreaming/etc.



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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-22 10:39                         ` Ananyev, Konstantin
@ 2022-02-22 13:46                           ` Thomas Monjalon
  2022-02-22 15:13                             ` Ananyev, Konstantin
  0 siblings, 1 reply; 46+ messages in thread
From: Thomas Monjalon @ 2022-02-22 13:46 UTC (permalink / raw)
  To: Stephen Hemminger, Medvedkin, Vladimir, dev, Morrissey, Sean,
	Richardson, Bruce, Ananyev, Konstantin
  Cc: dev

22/02/2022 11:39, Ananyev, Konstantin:
> 
> > > > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > > > >> to be as small as possible (it no longer is), and the real work should
> > > > > >> be done by libraries to make it easier to build other applications.
> > > > > >
> > > > > > I never heard users ask about such thing,
> > > > > > but if there is a demand for that, then I suppose it could be considered.
> > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > Though I believe it should be a subject of separate patch and discussion
> > > > > > (I think many questions will arise - what format should be, how to support
> > > > > > different types of user-data, to make it generic enough, etc.).
> > > > >
> > > > > Agree, it is very application specific, so it could be really difficult
> > > > > to make it generic.
> > > >
> > > > But several other also have LPM tables, so why not have common code for other applications.
> > > >
> > > > examples/l3fwd-power/main.c
> > > > examples/ipsec-secgw/rt.c
> > > > examples/ip_fragmentation/main.c
> > > > examples/l3fwd/l3fwd_lpm.c
> > > > examples/ip_reassembly/main.c
> > >
> > > Ah yes, that's good point.
> > > All these examples (except ipsec-secgw) started as l3fwd clones,
> > > so all of them have hard-coded LPM (and EM) tables too.
> > > Yes it would be good thing to address that problem too,
> > > and have some common code (and common routes file format) for all of them.
> > > I don't know is that a good idea to introduce parse file function in LPM/FIB library
> > > itself, might be better to  have something like examples/common/lpm_parse*.
> > > Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> > > My suggestion would be for 22.03 go ahead with current l3fwd patches,
> > > then later we can consider to make it common and update other examples.
> > 
> > I don't think this patch is urgent.
> > I suggest taking time to have common code for all examples
> > and target a merge in DPDK 22.07.
> 
> Well, yes, from one perspective it not really a critical one,
> we do live with hard-coded routes inside l3fwd for nearly 10 year by now.
> Though l3fwd is one of mostly used examples inside DPDK and
> it is quite a pain to patch/rebuild it each time someone needs to run
> l3fwd with a different routing table. 
> Merging this patch will allow people to use l3fwd for more realistic test
> scenarios in a painless manner.
> So I believe this patch is really helpful and should be beneficial for the whole community.
> Looking from that perspective, I don't see why it has to be "all or nothing" attitude here.
> Why we can't move one step at a time instead?
> That would allow to split and effort in terms of development/testing/upstreaming/etc.

When a feature is merged, there is less incentives to rework.
That's why, when a feature is not urgent,
it is better to wait for the complete work.




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

* RE: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-22 13:46                           ` Thomas Monjalon
@ 2022-02-22 15:13                             ` Ananyev, Konstantin
  2022-02-22 16:48                               ` Thomas Monjalon
  0 siblings, 1 reply; 46+ messages in thread
From: Ananyev, Konstantin @ 2022-02-22 15:13 UTC (permalink / raw)
  To: Thomas Monjalon, Stephen Hemminger, Medvedkin, Vladimir, dev,
	Morrissey, Sean, Richardson, Bruce
  Cc: dev



> >
> > > > > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > > > > >> to be as small as possible (it no longer is), and the real work should
> > > > > > >> be done by libraries to make it easier to build other applications.
> > > > > > >
> > > > > > > I never heard users ask about such thing,
> > > > > > > but if there is a demand for that, then I suppose it could be considered.
> > > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > > Though I believe it should be a subject of separate patch and discussion
> > > > > > > (I think many questions will arise - what format should be, how to support
> > > > > > > different types of user-data, to make it generic enough, etc.).
> > > > > >
> > > > > > Agree, it is very application specific, so it could be really difficult
> > > > > > to make it generic.
> > > > >
> > > > > But several other also have LPM tables, so why not have common code for other applications.
> > > > >
> > > > > examples/l3fwd-power/main.c
> > > > > examples/ipsec-secgw/rt.c
> > > > > examples/ip_fragmentation/main.c
> > > > > examples/l3fwd/l3fwd_lpm.c
> > > > > examples/ip_reassembly/main.c
> > > >
> > > > Ah yes, that's good point.
> > > > All these examples (except ipsec-secgw) started as l3fwd clones,
> > > > so all of them have hard-coded LPM (and EM) tables too.
> > > > Yes it would be good thing to address that problem too,
> > > > and have some common code (and common routes file format) for all of them.
> > > > I don't know is that a good idea to introduce parse file function in LPM/FIB library
> > > > itself, might be better to  have something like examples/common/lpm_parse*.
> > > > Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> > > > My suggestion would be for 22.03 go ahead with current l3fwd patches,
> > > > then later we can consider to make it common and update other examples.
> > >
> > > I don't think this patch is urgent.
> > > I suggest taking time to have common code for all examples
> > > and target a merge in DPDK 22.07.
> >
> > Well, yes, from one perspective it not really a critical one,
> > we do live with hard-coded routes inside l3fwd for nearly 10 year by now.
> > Though l3fwd is one of mostly used examples inside DPDK and
> > it is quite a pain to patch/rebuild it each time someone needs to run
> > l3fwd with a different routing table.
> > Merging this patch will allow people to use l3fwd for more realistic test
> > scenarios in a painless manner.
> > So I believe this patch is really helpful and should be beneficial for the whole community.
> > Looking from that perspective, I don't see why it has to be "all or nothing" attitude here.
> > Why we can't move one step at a time instead?
> > That would allow to split and effort in terms of development/testing/upstreaming/etc.
> 
> When a feature is merged, there is less incentives to rework.
> That's why, when a feature is not urgent,
> it is better to wait for the complete work.

That's true till some extent, though from other side
even without further rework that patch improves situation
from what we have right now.
So I don't see any harm here.


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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-22 15:13                             ` Ananyev, Konstantin
@ 2022-02-22 16:48                               ` Thomas Monjalon
  2022-02-24 11:06                                 ` Ananyev, Konstantin
  0 siblings, 1 reply; 46+ messages in thread
From: Thomas Monjalon @ 2022-02-22 16:48 UTC (permalink / raw)
  To: Stephen Hemminger, Medvedkin, Vladimir, dev, Morrissey, Sean,
	Richardson, Bruce, Ananyev, Konstantin
  Cc: dev

22/02/2022 16:13, Ananyev, Konstantin:
> 
> > >
> > > > > > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > > > > > >> to be as small as possible (it no longer is), and the real work should
> > > > > > > >> be done by libraries to make it easier to build other applications.
> > > > > > > >
> > > > > > > > I never heard users ask about such thing,
> > > > > > > > but if there is a demand for that, then I suppose it could be considered.
> > > > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > > > Though I believe it should be a subject of separate patch and discussion
> > > > > > > > (I think many questions will arise - what format should be, how to support
> > > > > > > > different types of user-data, to make it generic enough, etc.).
> > > > > > >
> > > > > > > Agree, it is very application specific, so it could be really difficult
> > > > > > > to make it generic.
> > > > > >
> > > > > > But several other also have LPM tables, so why not have common code for other applications.
> > > > > >
> > > > > > examples/l3fwd-power/main.c
> > > > > > examples/ipsec-secgw/rt.c
> > > > > > examples/ip_fragmentation/main.c
> > > > > > examples/l3fwd/l3fwd_lpm.c
> > > > > > examples/ip_reassembly/main.c
> > > > >
> > > > > Ah yes, that's good point.
> > > > > All these examples (except ipsec-secgw) started as l3fwd clones,
> > > > > so all of them have hard-coded LPM (and EM) tables too.
> > > > > Yes it would be good thing to address that problem too,
> > > > > and have some common code (and common routes file format) for all of them.
> > > > > I don't know is that a good idea to introduce parse file function in LPM/FIB library
> > > > > itself, might be better to  have something like examples/common/lpm_parse*.
> > > > > Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> > > > > My suggestion would be for 22.03 go ahead with current l3fwd patches,
> > > > > then later we can consider to make it common and update other examples.
> > > >
> > > > I don't think this patch is urgent.
> > > > I suggest taking time to have common code for all examples
> > > > and target a merge in DPDK 22.07.
> > >
> > > Well, yes, from one perspective it not really a critical one,
> > > we do live with hard-coded routes inside l3fwd for nearly 10 year by now.
> > > Though l3fwd is one of mostly used examples inside DPDK and
> > > it is quite a pain to patch/rebuild it each time someone needs to run
> > > l3fwd with a different routing table.
> > > Merging this patch will allow people to use l3fwd for more realistic test
> > > scenarios in a painless manner.
> > > So I believe this patch is really helpful and should be beneficial for the whole community.
> > > Looking from that perspective, I don't see why it has to be "all or nothing" attitude here.
> > > Why we can't move one step at a time instead?
> > > That would allow to split and effort in terms of development/testing/upstreaming/etc.
> > 
> > When a feature is merged, there is less incentives to rework.
> > That's why, when a feature is not urgent,
> > it is better to wait for the complete work.
> 
> That's true till some extent, though from other side
> even without further rework that patch improves situation
> from what we have right now.
> So I don't see any harm here.

It is adding a lot of code to an example which is already too big.
There are a lot of complain about the size of l3fwd.
That's why I think it makes sense to require this extra code
(not demonstrating anything, but just for testing convenience)
outside of the example.



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

* RE: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-22 16:48                               ` Thomas Monjalon
@ 2022-02-24 11:06                                 ` Ananyev, Konstantin
  2022-02-24 13:46                                   ` Thomas Monjalon
  0 siblings, 1 reply; 46+ messages in thread
From: Ananyev, Konstantin @ 2022-02-24 11:06 UTC (permalink / raw)
  To: Thomas Monjalon, Stephen Hemminger, Medvedkin, Vladimir, dev,
	Morrissey, Sean, Richardson, Bruce
  Cc: dev



> 
> 22/02/2022 16:13, Ananyev, Konstantin:
> >
> > > >
> > > > > > > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > > > > > > >> to be as small as possible (it no longer is), and the real work should
> > > > > > > > >> be done by libraries to make it easier to build other applications.
> > > > > > > > >
> > > > > > > > > I never heard users ask about such thing,
> > > > > > > > > but if there is a demand for that, then I suppose it could be considered.
> > > > > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > > > > Though I believe it should be a subject of separate patch and discussion
> > > > > > > > > (I think many questions will arise - what format should be, how to support
> > > > > > > > > different types of user-data, to make it generic enough, etc.).
> > > > > > > >
> > > > > > > > Agree, it is very application specific, so it could be really difficult
> > > > > > > > to make it generic.
> > > > > > >
> > > > > > > But several other also have LPM tables, so why not have common code for other applications.
> > > > > > >
> > > > > > > examples/l3fwd-power/main.c
> > > > > > > examples/ipsec-secgw/rt.c
> > > > > > > examples/ip_fragmentation/main.c
> > > > > > > examples/l3fwd/l3fwd_lpm.c
> > > > > > > examples/ip_reassembly/main.c
> > > > > >
> > > > > > Ah yes, that's good point.
> > > > > > All these examples (except ipsec-secgw) started as l3fwd clones,
> > > > > > so all of them have hard-coded LPM (and EM) tables too.
> > > > > > Yes it would be good thing to address that problem too,
> > > > > > and have some common code (and common routes file format) for all of them.
> > > > > > I don't know is that a good idea to introduce parse file function in LPM/FIB library
> > > > > > itself, might be better to  have something like examples/common/lpm_parse*.
> > > > > > Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> > > > > > My suggestion would be for 22.03 go ahead with current l3fwd patches,
> > > > > > then later we can consider to make it common and update other examples.
> > > > >
> > > > > I don't think this patch is urgent.
> > > > > I suggest taking time to have common code for all examples
> > > > > and target a merge in DPDK 22.07.
> > > >
> > > > Well, yes, from one perspective it not really a critical one,
> > > > we do live with hard-coded routes inside l3fwd for nearly 10 year by now.
> > > > Though l3fwd is one of mostly used examples inside DPDK and
> > > > it is quite a pain to patch/rebuild it each time someone needs to run
> > > > l3fwd with a different routing table.
> > > > Merging this patch will allow people to use l3fwd for more realistic test
> > > > scenarios in a painless manner.
> > > > So I believe this patch is really helpful and should be beneficial for the whole community.
> > > > Looking from that perspective, I don't see why it has to be "all or nothing" attitude here.
> > > > Why we can't move one step at a time instead?
> > > > That would allow to split and effort in terms of development/testing/upstreaming/etc.
> > >
> > > When a feature is merged, there is less incentives to rework.
> > > That's why, when a feature is not urgent,
> > > it is better to wait for the complete work.
> >
> > That's true till some extent, though from other side
> > even without further rework that patch improves situation
> > from what we have right now.
> > So I don't see any harm here.
> 
> It is adding a lot of code to an example which is already too big.
> There are a lot of complain about the size of l3fwd.
> That's why I think it makes sense to require this extra code
> (not demonstrating anything, but just for testing convenience)
> outside of the example.

Ok, so your main concern is l3fwd code size increase, right?
Then would it help if for we'll move file parsing code into a separate file(s)  
(under examples/l3fwd) for now?
Something like examples/l3fwd/(lpm_em)_route_parse.c.

  


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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-24 11:06                                 ` Ananyev, Konstantin
@ 2022-02-24 13:46                                   ` Thomas Monjalon
  2022-02-24 13:58                                     ` Bruce Richardson
  2022-02-25  5:18                                     ` Honnappa Nagarahalli
  0 siblings, 2 replies; 46+ messages in thread
From: Thomas Monjalon @ 2022-02-24 13:46 UTC (permalink / raw)
  To: Stephen Hemminger, Medvedkin, Vladimir, dev, Morrissey, Sean,
	Richardson, Bruce, Ananyev, Konstantin
  Cc: dev, honnappa.nagarahalli, jerinj, ajit.khaparde, olivier.matz,
	maxime.coquelin, david.marchand, ktraynor

24/02/2022 12:06, Ananyev, Konstantin:
> > > > > > > > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > > > > > > > >> to be as small as possible (it no longer is), and the real work should
> > > > > > > > > >> be done by libraries to make it easier to build other applications.
> > > > > > > > > >
> > > > > > > > > > I never heard users ask about such thing,
> > > > > > > > > > but if there is a demand for that, then I suppose it could be considered.
> > > > > > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > > > > > Though I believe it should be a subject of separate patch and discussion
> > > > > > > > > > (I think many questions will arise - what format should be, how to support
> > > > > > > > > > different types of user-data, to make it generic enough, etc.).
> > > > > > > > >
> > > > > > > > > Agree, it is very application specific, so it could be really difficult
> > > > > > > > > to make it generic.
> > > > > > > >
> > > > > > > > But several other also have LPM tables, so why not have common code for other applications.
> > > > > > > >
> > > > > > > > examples/l3fwd-power/main.c
> > > > > > > > examples/ipsec-secgw/rt.c
> > > > > > > > examples/ip_fragmentation/main.c
> > > > > > > > examples/l3fwd/l3fwd_lpm.c
> > > > > > > > examples/ip_reassembly/main.c
> > > > > > >
> > > > > > > Ah yes, that's good point.
> > > > > > > All these examples (except ipsec-secgw) started as l3fwd clones,
> > > > > > > so all of them have hard-coded LPM (and EM) tables too.
> > > > > > > Yes it would be good thing to address that problem too,
> > > > > > > and have some common code (and common routes file format) for all of them.
> > > > > > > I don't know is that a good idea to introduce parse file function in LPM/FIB library
> > > > > > > itself, might be better to  have something like examples/common/lpm_parse*.
> > > > > > > Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> > > > > > > My suggestion would be for 22.03 go ahead with current l3fwd patches,
> > > > > > > then later we can consider to make it common and update other examples.
> > > > > >
> > > > > > I don't think this patch is urgent.
> > > > > > I suggest taking time to have common code for all examples
> > > > > > and target a merge in DPDK 22.07.
> > > > >
> > > > > Well, yes, from one perspective it not really a critical one,
> > > > > we do live with hard-coded routes inside l3fwd for nearly 10 year by now.
> > > > > Though l3fwd is one of mostly used examples inside DPDK and
> > > > > it is quite a pain to patch/rebuild it each time someone needs to run
> > > > > l3fwd with a different routing table.
> > > > > Merging this patch will allow people to use l3fwd for more realistic test
> > > > > scenarios in a painless manner.
> > > > > So I believe this patch is really helpful and should be beneficial for the whole community.
> > > > > Looking from that perspective, I don't see why it has to be "all or nothing" attitude here.
> > > > > Why we can't move one step at a time instead?
> > > > > That would allow to split and effort in terms of development/testing/upstreaming/etc.
> > > >
> > > > When a feature is merged, there is less incentives to rework.
> > > > That's why, when a feature is not urgent,
> > > > it is better to wait for the complete work.
> > >
> > > That's true till some extent, though from other side
> > > even without further rework that patch improves situation
> > > from what we have right now.
> > > So I don't see any harm here.
> > 
> > It is adding a lot of code to an example which is already too big.
> > There are a lot of complain about the size of l3fwd.
> > That's why I think it makes sense to require this extra code
> > (not demonstrating anything, but just for testing convenience)
> > outside of the example.
> 
> Ok, so your main concern is l3fwd code size increase, right?
> Then would it help if for we'll move file parsing code into a separate file(s)  
> (under examples/l3fwd) for now?
> Something like examples/l3fwd/(lpm_em)_route_parse.c.

Yes it would help to isolate the config file parsing code.
What others think?



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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-24 13:46                                   ` Thomas Monjalon
@ 2022-02-24 13:58                                     ` Bruce Richardson
  2022-02-25 10:36                                       ` Ananyev, Konstantin
  2022-02-25  5:18                                     ` Honnappa Nagarahalli
  1 sibling, 1 reply; 46+ messages in thread
From: Bruce Richardson @ 2022-02-24 13:58 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Stephen Hemminger, Medvedkin, Vladimir, dev, Morrissey, Sean,
	Ananyev, Konstantin, honnappa.nagarahalli, jerinj, ajit.khaparde,
	olivier.matz, maxime.coquelin, david.marchand, ktraynor

On Thu, Feb 24, 2022 at 02:46:24PM +0100, Thomas Monjalon wrote:
> 24/02/2022 12:06, Ananyev, Konstantin:
> > > > > > > > > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > > > > > > > > >> to be as small as possible (it no longer is), and the real work should
> > > > > > > > > > >> be done by libraries to make it easier to build other applications.
> > > > > > > > > > >
> > > > > > > > > > > I never heard users ask about such thing,
> > > > > > > > > > > but if there is a demand for that, then I suppose it could be considered.
> > > > > > > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > > > > > > Though I believe it should be a subject of separate patch and discussion
> > > > > > > > > > > (I think many questions will arise - what format should be, how to support
> > > > > > > > > > > different types of user-data, to make it generic enough, etc.).
> > > > > > > > > >
> > > > > > > > > > Agree, it is very application specific, so it could be really difficult
> > > > > > > > > > to make it generic.
> > > > > > > > >
> > > > > > > > > But several other also have LPM tables, so why not have common code for other applications.
> > > > > > > > >
> > > > > > > > > examples/l3fwd-power/main.c
> > > > > > > > > examples/ipsec-secgw/rt.c
> > > > > > > > > examples/ip_fragmentation/main.c
> > > > > > > > > examples/l3fwd/l3fwd_lpm.c
> > > > > > > > > examples/ip_reassembly/main.c
> > > > > > > >
> > > > > > > > Ah yes, that's good point.
> > > > > > > > All these examples (except ipsec-secgw) started as l3fwd clones,
> > > > > > > > so all of them have hard-coded LPM (and EM) tables too.
> > > > > > > > Yes it would be good thing to address that problem too,
> > > > > > > > and have some common code (and common routes file format) for all of them.
> > > > > > > > I don't know is that a good idea to introduce parse file function in LPM/FIB library
> > > > > > > > itself, might be better to  have something like examples/common/lpm_parse*.
> > > > > > > > Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> > > > > > > > My suggestion would be for 22.03 go ahead with current l3fwd patches,
> > > > > > > > then later we can consider to make it common and update other examples.
> > > > > > >
> > > > > > > I don't think this patch is urgent.
> > > > > > > I suggest taking time to have common code for all examples
> > > > > > > and target a merge in DPDK 22.07.
> > > > > >
> > > > > > Well, yes, from one perspective it not really a critical one,
> > > > > > we do live with hard-coded routes inside l3fwd for nearly 10 year by now.
> > > > > > Though l3fwd is one of mostly used examples inside DPDK and
> > > > > > it is quite a pain to patch/rebuild it each time someone needs to run
> > > > > > l3fwd with a different routing table.
> > > > > > Merging this patch will allow people to use l3fwd for more realistic test
> > > > > > scenarios in a painless manner.
> > > > > > So I believe this patch is really helpful and should be beneficial for the whole community.
> > > > > > Looking from that perspective, I don't see why it has to be "all or nothing" attitude here.
> > > > > > Why we can't move one step at a time instead?
> > > > > > That would allow to split and effort in terms of development/testing/upstreaming/etc.
> > > > >
> > > > > When a feature is merged, there is less incentives to rework.
> > > > > That's why, when a feature is not urgent,
> > > > > it is better to wait for the complete work.
> > > >
> > > > That's true till some extent, though from other side
> > > > even without further rework that patch improves situation
> > > > from what we have right now.
> > > > So I don't see any harm here.
> > > 
> > > It is adding a lot of code to an example which is already too big.
> > > There are a lot of complain about the size of l3fwd.
> > > That's why I think it makes sense to require this extra code
> > > (not demonstrating anything, but just for testing convenience)
> > > outside of the example.
> > 
> > Ok, so your main concern is l3fwd code size increase, right?
> > Then would it help if for we'll move file parsing code into a separate file(s)  
> > (under examples/l3fwd) for now?
> > Something like examples/l3fwd/(lpm_em)_route_parse.c.
> 
> Yes it would help to isolate the config file parsing code.
> What others think?
> 
I still would like config code for loading an LPM table or FIB table to be
put inside the relevant libraries themselves, rather than having it in the
examples themselves (even if shared between them).

/Bruce

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

* RE: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-24 13:46                                   ` Thomas Monjalon
  2022-02-24 13:58                                     ` Bruce Richardson
@ 2022-02-25  5:18                                     ` Honnappa Nagarahalli
  1 sibling, 0 replies; 46+ messages in thread
From: Honnappa Nagarahalli @ 2022-02-25  5:18 UTC (permalink / raw)
  To: thomas, Stephen Hemminger, Medvedkin, Vladimir, dev, Morrissey,
	Sean, Richardson, Bruce, Ananyev, Konstantin
  Cc: dev, jerinj, Ajit Khaparde (ajit.khaparde@broadcom.com),
	olivier.matz, maxime.coquelin, david.marchand, ktraynor, nd, nd

<snip>

> 
> 24/02/2022 12:06, Ananyev, Konstantin:
> > > > > > > > > > >> Or have a generic library for reading LPM entries.
> > > > > > > > > > >> L3fwd is supposed to be as small as possible (it no
> > > > > > > > > > >> longer is), and the real work should be done by libraries to
> make it easier to build other applications.
> > > > > > > > > > >
> > > > > > > > > > > I never heard users ask about such thing, but if
> > > > > > > > > > > there is a demand for that, then I suppose it could be
> considered.
> > > > > > > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > > > > > > Though I believe it should be a subject of separate
> > > > > > > > > > > patch and discussion (I think many questions will
> > > > > > > > > > > arise - what format should be, how to support different types
> of user-data, to make it generic enough, etc.).
> > > > > > > > > >
> > > > > > > > > > Agree, it is very application specific, so it could be
> > > > > > > > > > really difficult to make it generic.
> > > > > > > > >
> > > > > > > > > But several other also have LPM tables, so why not have common
> code for other applications.
> > > > > > > > >
> > > > > > > > > examples/l3fwd-power/main.c examples/ipsec-secgw/rt.c
> > > > > > > > > examples/ip_fragmentation/main.c
> > > > > > > > > examples/l3fwd/l3fwd_lpm.c examples/ip_reassembly/main.c
> > > > > > > >
> > > > > > > > Ah yes, that's good point.
> > > > > > > > All these examples (except ipsec-secgw) started as l3fwd
> > > > > > > > clones, so all of them have hard-coded LPM (and EM) tables too.
> > > > > > > > Yes it would be good thing to address that problem too,
> > > > > > > > and have some common code (and common routes file format) for
> all of them.
> > > > > > > > I don't know is that a good idea to introduce parse file
> > > > > > > > function in LPM/FIB library itself, might be better to  have
> something like examples/common/lpm_parse*.
> > > > > > > > Anyway, this is an extra effort, and I think no-one has time for it in
> 22.03 timeframe.
> > > > > > > > My suggestion would be for 22.03 go ahead with current
> > > > > > > > l3fwd patches, then later we can consider to make it common and
> update other examples.
> > > > > > >
> > > > > > > I don't think this patch is urgent.
> > > > > > > I suggest taking time to have common code for all examples
> > > > > > > and target a merge in DPDK 22.07.
> > > > > >
> > > > > > Well, yes, from one perspective it not really a critical one,
> > > > > > we do live with hard-coded routes inside l3fwd for nearly 10 year by
> now.
> > > > > > Though l3fwd is one of mostly used examples inside DPDK and it
> > > > > > is quite a pain to patch/rebuild it each time someone needs to
> > > > > > run l3fwd with a different routing table.
> > > > > > Merging this patch will allow people to use l3fwd for more
> > > > > > realistic test scenarios in a painless manner.
> > > > > > So I believe this patch is really helpful and should be beneficial for the
> whole community.
> > > > > > Looking from that perspective, I don't see why it has to be "all or
> nothing" attitude here.
> > > > > > Why we can't move one step at a time instead?
> > > > > > That would allow to split and effort in terms of
> development/testing/upstreaming/etc.
> > > > >
> > > > > When a feature is merged, there is less incentives to rework.
> > > > > That's why, when a feature is not urgent, it is better to wait
> > > > > for the complete work.
> > > >
> > > > That's true till some extent, though from other side even without
> > > > further rework that patch improves situation from what we have
> > > > right now.
> > > > So I don't see any harm here.
> > >
> > > It is adding a lot of code to an example which is already too big.
> > > There are a lot of complain about the size of l3fwd.
> > > That's why I think it makes sense to require this extra code (not
> > > demonstrating anything, but just for testing convenience) outside of
> > > the example.
> >
> > Ok, so your main concern is l3fwd code size increase, right?
> > Then would it help if for we'll move file parsing code into a separate
> > file(s) (under examples/l3fwd) for now?
> > Something like examples/l3fwd/(lpm_em)_route_parse.c.
> 
> Yes it would help to isolate the config file parsing code.
> What others think?
L3fwd is no more a sample application, suggest moving it to app directory.

> 


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

* RE: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-24 13:58                                     ` Bruce Richardson
@ 2022-02-25 10:36                                       ` Ananyev, Konstantin
  2022-02-25 10:40                                         ` Bruce Richardson
  0 siblings, 1 reply; 46+ messages in thread
From: Ananyev, Konstantin @ 2022-02-25 10:36 UTC (permalink / raw)
  To: Richardson, Bruce, Thomas Monjalon
  Cc: Stephen Hemminger, Medvedkin, Vladimir, dev, Morrissey, Sean,
	honnappa.nagarahalli, jerinj, ajit.khaparde, olivier.matz,
	maxime.coquelin, david.marchand, ktraynor


> On Thu, Feb 24, 2022 at 02:46:24PM +0100, Thomas Monjalon wrote:
> > 24/02/2022 12:06, Ananyev, Konstantin:
> > > > > > > > > > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > > > > > > > > > >> to be as small as possible (it no longer is), and the real work should
> > > > > > > > > > > >> be done by libraries to make it easier to build other applications.
> > > > > > > > > > > >
> > > > > > > > > > > > I never heard users ask about such thing,
> > > > > > > > > > > > but if there is a demand for that, then I suppose it could be considered.
> > > > > > > > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > > > > > > > Though I believe it should be a subject of separate patch and discussion
> > > > > > > > > > > > (I think many questions will arise - what format should be, how to support
> > > > > > > > > > > > different types of user-data, to make it generic enough, etc.).
> > > > > > > > > > >
> > > > > > > > > > > Agree, it is very application specific, so it could be really difficult
> > > > > > > > > > > to make it generic.
> > > > > > > > > >
> > > > > > > > > > But several other also have LPM tables, so why not have common code for other applications.
> > > > > > > > > >
> > > > > > > > > > examples/l3fwd-power/main.c
> > > > > > > > > > examples/ipsec-secgw/rt.c
> > > > > > > > > > examples/ip_fragmentation/main.c
> > > > > > > > > > examples/l3fwd/l3fwd_lpm.c
> > > > > > > > > > examples/ip_reassembly/main.c
> > > > > > > > >
> > > > > > > > > Ah yes, that's good point.
> > > > > > > > > All these examples (except ipsec-secgw) started as l3fwd clones,
> > > > > > > > > so all of them have hard-coded LPM (and EM) tables too.
> > > > > > > > > Yes it would be good thing to address that problem too,
> > > > > > > > > and have some common code (and common routes file format) for all of them.
> > > > > > > > > I don't know is that a good idea to introduce parse file function in LPM/FIB library
> > > > > > > > > itself, might be better to  have something like examples/common/lpm_parse*.
> > > > > > > > > Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> > > > > > > > > My suggestion would be for 22.03 go ahead with current l3fwd patches,
> > > > > > > > > then later we can consider to make it common and update other examples.
> > > > > > > >
> > > > > > > > I don't think this patch is urgent.
> > > > > > > > I suggest taking time to have common code for all examples
> > > > > > > > and target a merge in DPDK 22.07.
> > > > > > >
> > > > > > > Well, yes, from one perspective it not really a critical one,
> > > > > > > we do live with hard-coded routes inside l3fwd for nearly 10 year by now.
> > > > > > > Though l3fwd is one of mostly used examples inside DPDK and
> > > > > > > it is quite a pain to patch/rebuild it each time someone needs to run
> > > > > > > l3fwd with a different routing table.
> > > > > > > Merging this patch will allow people to use l3fwd for more realistic test
> > > > > > > scenarios in a painless manner.
> > > > > > > So I believe this patch is really helpful and should be beneficial for the whole community.
> > > > > > > Looking from that perspective, I don't see why it has to be "all or nothing" attitude here.
> > > > > > > Why we can't move one step at a time instead?
> > > > > > > That would allow to split and effort in terms of development/testing/upstreaming/etc.
> > > > > >
> > > > > > When a feature is merged, there is less incentives to rework.
> > > > > > That's why, when a feature is not urgent,
> > > > > > it is better to wait for the complete work.
> > > > >
> > > > > That's true till some extent, though from other side
> > > > > even without further rework that patch improves situation
> > > > > from what we have right now.
> > > > > So I don't see any harm here.
> > > >
> > > > It is adding a lot of code to an example which is already too big.
> > > > There are a lot of complain about the size of l3fwd.
> > > > That's why I think it makes sense to require this extra code
> > > > (not demonstrating anything, but just for testing convenience)
> > > > outside of the example.
> > >
> > > Ok, so your main concern is l3fwd code size increase, right?
> > > Then would it help if for we'll move file parsing code into a separate file(s)
> > > (under examples/l3fwd) for now?
> > > Something like examples/l3fwd/(lpm_em)_route_parse.c.
> >
> > Yes it would help to isolate the config file parsing code.
> > What others think?
> >
> I still would like config code for loading an LPM table or FIB table to be
> put inside the relevant libraries themselves, rather than having it in the
> examples themselves (even if shared between them).

Honestly, I don't see any good reasons for that:
I presume users of these libraries already have their own routing
config files with their own format requirements and I suppose
these formats differ a lot (depending on use-case).
For DPDK internal purposes (provide sample apps with ability to load routes dynamically)
some common code inside examples/ seems more than enough. 
Konstantin




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

* Re: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-25 10:36                                       ` Ananyev, Konstantin
@ 2022-02-25 10:40                                         ` Bruce Richardson
  2022-02-25 12:21                                           ` Ananyev, Konstantin
  2022-02-25 12:50                                           ` Morten Brørup
  0 siblings, 2 replies; 46+ messages in thread
From: Bruce Richardson @ 2022-02-25 10:40 UTC (permalink / raw)
  To: Ananyev, Konstantin
  Cc: Thomas Monjalon, Stephen Hemminger, Medvedkin, Vladimir, dev,
	Morrissey, Sean, honnappa.nagarahalli, jerinj, ajit.khaparde,
	olivier.matz, maxime.coquelin, david.marchand, ktraynor

On Fri, Feb 25, 2022 at 10:36:29AM +0000, Ananyev, Konstantin wrote:
> 
> > On Thu, Feb 24, 2022 at 02:46:24PM +0100, Thomas Monjalon wrote:
> > > 24/02/2022 12:06, Ananyev, Konstantin:
> > > > > > > > > > > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > > > > > > > > > > >> to be as small as possible (it no longer is), and the real work should
> > > > > > > > > > > > >> be done by libraries to make it easier to build other applications.
> > > > > > > > > > > > >
> > > > > > > > > > > > > I never heard users ask about such thing,
> > > > > > > > > > > > > but if there is a demand for that, then I suppose it could be considered.
> > > > > > > > > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > > > > > > > > Though I believe it should be a subject of separate patch and discussion
> > > > > > > > > > > > > (I think many questions will arise - what format should be, how to support
> > > > > > > > > > > > > different types of user-data, to make it generic enough, etc.).
> > > > > > > > > > > >
> > > > > > > > > > > > Agree, it is very application specific, so it could be really difficult
> > > > > > > > > > > > to make it generic.
> > > > > > > > > > >
> > > > > > > > > > > But several other also have LPM tables, so why not have common code for other applications.
> > > > > > > > > > >
> > > > > > > > > > > examples/l3fwd-power/main.c
> > > > > > > > > > > examples/ipsec-secgw/rt.c
> > > > > > > > > > > examples/ip_fragmentation/main.c
> > > > > > > > > > > examples/l3fwd/l3fwd_lpm.c
> > > > > > > > > > > examples/ip_reassembly/main.c
> > > > > > > > > >
> > > > > > > > > > Ah yes, that's good point.
> > > > > > > > > > All these examples (except ipsec-secgw) started as l3fwd clones,
> > > > > > > > > > so all of them have hard-coded LPM (and EM) tables too.
> > > > > > > > > > Yes it would be good thing to address that problem too,
> > > > > > > > > > and have some common code (and common routes file format) for all of them.
> > > > > > > > > > I don't know is that a good idea to introduce parse file function in LPM/FIB library
> > > > > > > > > > itself, might be better to  have something like examples/common/lpm_parse*.
> > > > > > > > > > Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> > > > > > > > > > My suggestion would be for 22.03 go ahead with current l3fwd patches,
> > > > > > > > > > then later we can consider to make it common and update other examples.
> > > > > > > > >
> > > > > > > > > I don't think this patch is urgent.
> > > > > > > > > I suggest taking time to have common code for all examples
> > > > > > > > > and target a merge in DPDK 22.07.
> > > > > > > >
> > > > > > > > Well, yes, from one perspective it not really a critical one,
> > > > > > > > we do live with hard-coded routes inside l3fwd for nearly 10 year by now.
> > > > > > > > Though l3fwd is one of mostly used examples inside DPDK and
> > > > > > > > it is quite a pain to patch/rebuild it each time someone needs to run
> > > > > > > > l3fwd with a different routing table.
> > > > > > > > Merging this patch will allow people to use l3fwd for more realistic test
> > > > > > > > scenarios in a painless manner.
> > > > > > > > So I believe this patch is really helpful and should be beneficial for the whole community.
> > > > > > > > Looking from that perspective, I don't see why it has to be "all or nothing" attitude here.
> > > > > > > > Why we can't move one step at a time instead?
> > > > > > > > That would allow to split and effort in terms of development/testing/upstreaming/etc.
> > > > > > >
> > > > > > > When a feature is merged, there is less incentives to rework.
> > > > > > > That's why, when a feature is not urgent,
> > > > > > > it is better to wait for the complete work.
> > > > > >
> > > > > > That's true till some extent, though from other side
> > > > > > even without further rework that patch improves situation
> > > > > > from what we have right now.
> > > > > > So I don't see any harm here.
> > > > >
> > > > > It is adding a lot of code to an example which is already too big.
> > > > > There are a lot of complain about the size of l3fwd.
> > > > > That's why I think it makes sense to require this extra code
> > > > > (not demonstrating anything, but just for testing convenience)
> > > > > outside of the example.
> > > >
> > > > Ok, so your main concern is l3fwd code size increase, right?
> > > > Then would it help if for we'll move file parsing code into a separate file(s)
> > > > (under examples/l3fwd) for now?
> > > > Something like examples/l3fwd/(lpm_em)_route_parse.c.
> > >
> > > Yes it would help to isolate the config file parsing code.
> > > What others think?
> > >
> > I still would like config code for loading an LPM table or FIB table to be
> > put inside the relevant libraries themselves, rather than having it in the
> > examples themselves (even if shared between them).
> 
> Honestly, I don't see any good reasons for that:
> I presume users of these libraries already have their own routing
> config files with their own format requirements and I suppose
> these formats differ a lot (depending on use-case).

Yes, I agree that all existing users of the libraries will have this in
place. However, for new users, this may be useful for bootstrapping things,
and it also makes it generally useable for both example apps, and any apps
in the "app" folder, i.e. if l3fwd gets moved there. (Something I agree
with, as it's now more than just example code).


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

* RE: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-25 10:40                                         ` Bruce Richardson
@ 2022-02-25 12:21                                           ` Ananyev, Konstantin
  2022-02-25 12:50                                           ` Morten Brørup
  1 sibling, 0 replies; 46+ messages in thread
From: Ananyev, Konstantin @ 2022-02-25 12:21 UTC (permalink / raw)
  To: Richardson, Bruce
  Cc: Thomas Monjalon, Stephen Hemminger, Medvedkin, Vladimir, dev,
	Morrissey, Sean, honnappa.nagarahalli, jerinj, ajit.khaparde,
	olivier.matz, maxime.coquelin, david.marchand, ktraynor


> > > > > > > > > > > > > >> Or have a generic library for reading LPM entries.  L3fwd is supposed
> > > > > > > > > > > > > >> to be as small as possible (it no longer is), and the real work should
> > > > > > > > > > > > > >> be done by libraries to make it easier to build other applications.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > I never heard users ask about such thing,
> > > > > > > > > > > > > > but if there is a demand for that, then I suppose it could be considered.
> > > > > > > > > > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > > > > > > > > > Though I believe it should be a subject of separate patch and discussion
> > > > > > > > > > > > > > (I think many questions will arise - what format should be, how to support
> > > > > > > > > > > > > > different types of user-data, to make it generic enough, etc.).
> > > > > > > > > > > > >
> > > > > > > > > > > > > Agree, it is very application specific, so it could be really difficult
> > > > > > > > > > > > > to make it generic.
> > > > > > > > > > > >
> > > > > > > > > > > > But several other also have LPM tables, so why not have common code for other applications.
> > > > > > > > > > > >
> > > > > > > > > > > > examples/l3fwd-power/main.c
> > > > > > > > > > > > examples/ipsec-secgw/rt.c
> > > > > > > > > > > > examples/ip_fragmentation/main.c
> > > > > > > > > > > > examples/l3fwd/l3fwd_lpm.c
> > > > > > > > > > > > examples/ip_reassembly/main.c
> > > > > > > > > > >
> > > > > > > > > > > Ah yes, that's good point.
> > > > > > > > > > > All these examples (except ipsec-secgw) started as l3fwd clones,
> > > > > > > > > > > so all of them have hard-coded LPM (and EM) tables too.
> > > > > > > > > > > Yes it would be good thing to address that problem too,
> > > > > > > > > > > and have some common code (and common routes file format) for all of them.
> > > > > > > > > > > I don't know is that a good idea to introduce parse file function in LPM/FIB library
> > > > > > > > > > > itself, might be better to  have something like examples/common/lpm_parse*.
> > > > > > > > > > > Anyway, this is an extra effort, and I think no-one has time for it in 22.03 timeframe.
> > > > > > > > > > > My suggestion would be for 22.03 go ahead with current l3fwd patches,
> > > > > > > > > > > then later we can consider to make it common and update other examples.
> > > > > > > > > >
> > > > > > > > > > I don't think this patch is urgent.
> > > > > > > > > > I suggest taking time to have common code for all examples
> > > > > > > > > > and target a merge in DPDK 22.07.
> > > > > > > > >
> > > > > > > > > Well, yes, from one perspective it not really a critical one,
> > > > > > > > > we do live with hard-coded routes inside l3fwd for nearly 10 year by now.
> > > > > > > > > Though l3fwd is one of mostly used examples inside DPDK and
> > > > > > > > > it is quite a pain to patch/rebuild it each time someone needs to run
> > > > > > > > > l3fwd with a different routing table.
> > > > > > > > > Merging this patch will allow people to use l3fwd for more realistic test
> > > > > > > > > scenarios in a painless manner.
> > > > > > > > > So I believe this patch is really helpful and should be beneficial for the whole community.
> > > > > > > > > Looking from that perspective, I don't see why it has to be "all or nothing" attitude here.
> > > > > > > > > Why we can't move one step at a time instead?
> > > > > > > > > That would allow to split and effort in terms of development/testing/upstreaming/etc.
> > > > > > > >
> > > > > > > > When a feature is merged, there is less incentives to rework.
> > > > > > > > That's why, when a feature is not urgent,
> > > > > > > > it is better to wait for the complete work.
> > > > > > >
> > > > > > > That's true till some extent, though from other side
> > > > > > > even without further rework that patch improves situation
> > > > > > > from what we have right now.
> > > > > > > So I don't see any harm here.
> > > > > >
> > > > > > It is adding a lot of code to an example which is already too big.
> > > > > > There are a lot of complain about the size of l3fwd.
> > > > > > That's why I think it makes sense to require this extra code
> > > > > > (not demonstrating anything, but just for testing convenience)
> > > > > > outside of the example.
> > > > >
> > > > > Ok, so your main concern is l3fwd code size increase, right?
> > > > > Then would it help if for we'll move file parsing code into a separate file(s)
> > > > > (under examples/l3fwd) for now?
> > > > > Something like examples/l3fwd/(lpm_em)_route_parse.c.
> > > >
> > > > Yes it would help to isolate the config file parsing code.
> > > > What others think?
> > > >
> > > I still would like config code for loading an LPM table or FIB table to be
> > > put inside the relevant libraries themselves, rather than having it in the
> > > examples themselves (even if shared between them).
> >
> > Honestly, I don't see any good reasons for that:
> > I presume users of these libraries already have their own routing
> > config files with their own format requirements and I suppose
> > these formats differ a lot (depending on use-case).
> 
> Yes, I agree that all existing users of the libraries will have this in
> place. However, for new users, this may be useful for bootstrapping things,
> and it also makes it generally useable for both example apps, and any apps
> in the "app" folder, i.e. if l3fwd gets moved there. (Something I agree
> with, as it's now more than just example code).

Inside dpdk for l3fwd like examples, all we need as data associated
with route is just a port number.
I really doubt that for real-world app that would be all they need -
I expect users would like to associate much more data with each route.
So I don't see how such library function will be useful even for new apps.
From other side, if we will put sample code in examples/l3fwd/*,
users can copy it from there and use it as a starting point for their
specific route config parser.
  



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

* RE: [PATCH v5 0/2] Add config file support for l3fwd
  2022-02-25 10:40                                         ` Bruce Richardson
  2022-02-25 12:21                                           ` Ananyev, Konstantin
@ 2022-02-25 12:50                                           ` Morten Brørup
  1 sibling, 0 replies; 46+ messages in thread
From: Morten Brørup @ 2022-02-25 12:50 UTC (permalink / raw)
  To: Bruce Richardson, Ananyev, Konstantin
  Cc: Thomas Monjalon, Stephen Hemminger, Medvedkin, Vladimir, dev,
	Morrissey, Sean, honnappa.nagarahalli, jerinj, ajit.khaparde,
	olivier.matz, maxime.coquelin, david.marchand, ktraynor

> From: Bruce Richardson [mailto:bruce.richardson@intel.com]
> Sent: Friday, 25 February 2022 11.40
> 
> On Fri, Feb 25, 2022 at 10:36:29AM +0000, Ananyev, Konstantin wrote:
> >
> > > On Thu, Feb 24, 2022 at 02:46:24PM +0100, Thomas Monjalon wrote:
> > > > 24/02/2022 12:06, Ananyev, Konstantin:
> > > > > > > > > > > > > >> Or have a generic library for reading LPM
> entries.  L3fwd is supposed
> > > > > > > > > > > > > >> to be as small as possible (it no longer
> is), and the real work should
> > > > > > > > > > > > > >> be done by libraries to make it easier to
> build other applications.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > I never heard users ask about such thing,
> > > > > > > > > > > > > > but if there is a demand for that, then I
> suppose it could be considered.
> > > > > > > > > > > > > > CC-ing LPM/FIB maintainers to comment.
> > > > > > > > > > > > > > Though I believe it should be a subject of
> separate patch and discussion
> > > > > > > > > > > > > > (I think many questions will arise - what
> format should be, how to support
> > > > > > > > > > > > > > different types of user-data, to make it
> generic enough, etc.).
> > > > > > > > > > > > >
> > > > > > > > > > > > > Agree, it is very application specific, so it
> could be really difficult
> > > > > > > > > > > > > to make it generic.
> > > > > > > > > > > >
> > > > > > > > > > > > But several other also have LPM tables, so why
> not have common code for other applications.
> > > > > > > > > > > >
> > > > > > > > > > > > examples/l3fwd-power/main.c
> > > > > > > > > > > > examples/ipsec-secgw/rt.c
> > > > > > > > > > > > examples/ip_fragmentation/main.c
> > > > > > > > > > > > examples/l3fwd/l3fwd_lpm.c
> > > > > > > > > > > > examples/ip_reassembly/main.c
> > > > > > > > > > >
> > > > > > > > > > > Ah yes, that's good point.
> > > > > > > > > > > All these examples (except ipsec-secgw) started as
> l3fwd clones,
> > > > > > > > > > > so all of them have hard-coded LPM (and EM) tables
> too.
> > > > > > > > > > > Yes it would be good thing to address that problem
> too,
> > > > > > > > > > > and have some common code (and common routes file
> format) for all of them.
> > > > > > > > > > > I don't know is that a good idea to introduce parse
> file function in LPM/FIB library
> > > > > > > > > > > itself, might be better to  have something like
> examples/common/lpm_parse*.
> > > > > > > > > > > Anyway, this is an extra effort, and I think no-one
> has time for it in 22.03 timeframe.
> > > > > > > > > > > My suggestion would be for 22.03 go ahead with
> current l3fwd patches,
> > > > > > > > > > > then later we can consider to make it common and
> update other examples.
> > > > > > > > > >
> > > > > > > > > > I don't think this patch is urgent.
> > > > > > > > > > I suggest taking time to have common code for all
> examples
> > > > > > > > > > and target a merge in DPDK 22.07.
> > > > > > > > >
> > > > > > > > > Well, yes, from one perspective it not really a
> critical one,
> > > > > > > > > we do live with hard-coded routes inside l3fwd for
> nearly 10 year by now.
> > > > > > > > > Though l3fwd is one of mostly used examples inside DPDK
> and
> > > > > > > > > it is quite a pain to patch/rebuild it each time
> someone needs to run
> > > > > > > > > l3fwd with a different routing table.
> > > > > > > > > Merging this patch will allow people to use l3fwd for
> more realistic test
> > > > > > > > > scenarios in a painless manner.
> > > > > > > > > So I believe this patch is really helpful and should be
> beneficial for the whole community.
> > > > > > > > > Looking from that perspective, I don't see why it has
> to be "all or nothing" attitude here.
> > > > > > > > > Why we can't move one step at a time instead?
> > > > > > > > > That would allow to split and effort in terms of
> development/testing/upstreaming/etc.
> > > > > > > >
> > > > > > > > When a feature is merged, there is less incentives to
> rework.
> > > > > > > > That's why, when a feature is not urgent,
> > > > > > > > it is better to wait for the complete work.
> > > > > > >
> > > > > > > That's true till some extent, though from other side
> > > > > > > even without further rework that patch improves situation
> > > > > > > from what we have right now.
> > > > > > > So I don't see any harm here.
> > > > > >
> > > > > > It is adding a lot of code to an example which is already too
> big.
> > > > > > There are a lot of complain about the size of l3fwd.
> > > > > > That's why I think it makes sense to require this extra code
> > > > > > (not demonstrating anything, but just for testing
> convenience)
> > > > > > outside of the example.
> > > > >
> > > > > Ok, so your main concern is l3fwd code size increase, right?
> > > > > Then would it help if for we'll move file parsing code into a
> separate file(s)
> > > > > (under examples/l3fwd) for now?
> > > > > Something like examples/l3fwd/(lpm_em)_route_parse.c.
> > > >
> > > > Yes it would help to isolate the config file parsing code.
> > > > What others think?
> > > >
> > > I still would like config code for loading an LPM table or FIB
> table to be
> > > put inside the relevant libraries themselves, rather than having it
> in the
> > > examples themselves (even if shared between them).
> >
> > Honestly, I don't see any good reasons for that:
> > I presume users of these libraries already have their own routing
> > config files with their own format requirements and I suppose
> > these formats differ a lot (depending on use-case).

I can confirm this assumption.

Our appliances use our own configuration file handling, which supports full, partial and diff configurations. Furthermore, it uses a schema with type definitions for simple syntax checking, as well as a programmable validator to check system-wide configuration validity and integrity.

> 
> Yes, I agree that all existing users of the libraries will have this in
> place. However, for new users, this may be useful for bootstrapping
> things,
> and it also makes it generally useable for both example apps, and any
> apps
> in the "app" folder, i.e. if l3fwd gets moved there. (Something I agree
> with, as it's now more than just example code).
> 

Don't put any config file parsers or similar in the fast path libraries. It is unwanted bloat!

If you want to standardize on a DPDK specific config file format for the DPDK provided examples and applications, which I do agree with the benefits of having, then provide a separate library to interact with the underlying fast path libraries.


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

* [PATCH v6 0/2] Add config file support for l3fwd
  2022-02-04 19:59       ` [PATCH v5 0/2] Add config file support for l3fwd Sean Morrissey
                           ` (2 preceding siblings ...)
  2022-02-04 22:26         ` [PATCH v5 0/2] Add config file support for l3fwd Stephen Hemminger
@ 2022-03-01 14:49         ` Sean Morrissey
  2022-03-01 14:49           ` [PATCH v6 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
                             ` (2 more replies)
  3 siblings, 3 replies; 46+ messages in thread
From: Sean Morrissey @ 2022-03-01 14:49 UTC (permalink / raw)
  Cc: dev, Sean Morrissey

This patchset introduces config file support for l3fwd
and its lookup methods LPM, FIB, and EM, similar to
that of l3fwd-acl. This allows for route rules to be
defined in configuration files and edited there instead
of in each of the lookup methods hardcoded route tables.

V6:
* Move config file parsing functions to separate files
  as to not bloat l3fwd code.
V5:
* Reintroduce hardcoded tables as to not break dts and
  allow for hardcoded tables to be used if no config
  files presented.
V4:
* Fix nondeterministic bug of segfault on termination of
  sample app.

Sean Morrissey (2):
  examples/l3fwd: add config file support for LPM/FIB
  examples/l3fwd: add config file support for EM

 doc/guides/sample_app_ug/l3_forward.rst |  89 +++++--
 examples/l3fwd/em_default_v4.cfg        |  17 ++
 examples/l3fwd/em_default_v6.cfg        |  17 ++
 examples/l3fwd/em_route_parse.c         | 280 ++++++++++++++++++++++
 examples/l3fwd/l3fwd.h                  |   7 +
 examples/l3fwd/l3fwd_em.c               | 220 ++++++-----------
 examples/l3fwd/l3fwd_fib.c              |  50 ++--
 examples/l3fwd/l3fwd_lpm.c              |  46 ++--
 examples/l3fwd/l3fwd_route.h            |  89 ++++++-
 examples/l3fwd/lpm_default_v4.cfg       |  17 ++
 examples/l3fwd/lpm_default_v6.cfg       |  17 ++
 examples/l3fwd/lpm_route_parse.c        | 302 ++++++++++++++++++++++++
 examples/l3fwd/main.c                   |  50 +++-
 13 files changed, 992 insertions(+), 209 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg
 create mode 100644 examples/l3fwd/em_route_parse.c
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg
 create mode 100644 examples/l3fwd/lpm_route_parse.c

-- 
2.25.1


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

* [PATCH v6 1/2] examples/l3fwd: add config file support for LPM/FIB
  2022-03-01 14:49         ` [PATCH v6 " Sean Morrissey
@ 2022-03-01 14:49           ` Sean Morrissey
  2022-03-01 14:49           ` [PATCH v6 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
  2022-03-08  8:57           ` [PATCH v6 0/2] Add config file support for l3fwd Thomas Monjalon
  2 siblings, 0 replies; 46+ messages in thread
From: Sean Morrissey @ 2022-03-01 14:49 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Konstantin Ananyev, Yingya Han

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for LPM and FIB,
with format similar to l3fwd-acl one.

Users can now use the default hardcoded route tables
or optionally config files. Default config files have
been provided for use with LPM and FIB.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Tested-by: Yingya Han <yingyax.han@intel.com>
---
 examples/l3fwd/em_route_parse.c   |  18 ++
 examples/l3fwd/l3fwd.h            |   7 +
 examples/l3fwd/l3fwd_em.c         |   1 +
 examples/l3fwd/l3fwd_fib.c        |  50 ++---
 examples/l3fwd/l3fwd_lpm.c        |  46 ++---
 examples/l3fwd/l3fwd_route.h      |  51 +++++
 examples/l3fwd/lpm_default_v4.cfg |  17 ++
 examples/l3fwd/lpm_default_v6.cfg |  17 ++
 examples/l3fwd/lpm_route_parse.c  | 302 ++++++++++++++++++++++++++++++
 examples/l3fwd/main.c             |  50 ++++-
 10 files changed, 515 insertions(+), 44 deletions(-)
 create mode 100644 examples/l3fwd/em_route_parse.c
 create mode 100644 examples/l3fwd/lpm_default_v4.cfg
 create mode 100644 examples/l3fwd/lpm_default_v6.cfg
 create mode 100644 examples/l3fwd/lpm_route_parse.c

diff --git a/examples/l3fwd/em_route_parse.c b/examples/l3fwd/em_route_parse.c
new file mode 100644
index 0000000000..11d83d9b42
--- /dev/null
+++ b/examples/l3fwd/em_route_parse.c
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+
+#include "l3fwd_route.h"
+
+void
+em_free_routes(void)
+{
+	/* Empty till config file support added to EM */
+}
+
+/* Load rules from the input file */
+void
+read_config_files_em(void)
+{
+	/* Empty till config file support added to EM */
+}
diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index 38ca19133c..002510b851 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -58,6 +58,11 @@
 #endif
 #define HASH_ENTRY_NUMBER_DEFAULT	16
 
+struct parm_cfg {
+	const char *rule_ipv4_name;
+	const char *rule_ipv6_name;
+};
+
 struct mbuf_table {
 	uint16_t len;
 	struct rte_mbuf *m_table[MAX_PKT_BURST];
@@ -96,6 +101,8 @@ extern xmm_t val_eth[RTE_MAX_ETHPORTS];
 
 extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+extern struct parm_cfg parm_config;
+
 /* Send burst of packets on an output interface */
 static inline int
 send_burst(struct lcore_conf *qconf, uint16_t n, uint16_t port)
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index 5cc4a4d979..de5be9e0a4 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -27,6 +27,7 @@
 
 #include "l3fwd.h"
 #include "l3fwd_event.h"
+#include "em_route_parse.c"
 
 #if defined(RTE_ARCH_X86) || defined(__ARM_FEATURE_CRC32)
 #define EM_HASH_CRC 1
diff --git a/examples/l3fwd/l3fwd_fib.c b/examples/l3fwd/l3fwd_fib.c
index 2110459cc3..6e0054b4cb 100644
--- a/examples/l3fwd/l3fwd_fib.c
+++ b/examples/l3fwd/l3fwd_fib.c
@@ -583,7 +583,7 @@ setup_fib(const int socketid)
 	struct rte_eth_dev_info dev_info;
 	struct rte_fib6_conf config;
 	struct rte_fib_conf config_ipv4;
-	unsigned int i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -603,37 +603,39 @@ setup_fib(const int socketid)
 			"Unable to create the l3fwd FIB table on socket %d\n",
 			socketid);
 
+
 	/* Populate the fib ipv4 table. */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* Skip unused ports. */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_fib_add(ipv4_l3fwd_fib_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		if (inet_ntop(AF_INET, &in, abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv4_l3fwd_route_array[i].depth,
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].depth,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv4 route added to port %d [%s]\n",
-			       ipv4_l3fwd_route_array[i].if_out,
+			       route_base_v4[i].if_out,
 			       dev_info.device->name);
 		}
 	}
@@ -650,41 +652,45 @@ setup_fib(const int socketid)
 	config.trie.num_tbl8 = (1 << 15);
 	ipv6_l3fwd_fib_lookup_struct[socketid] = rte_fib6_create(s, socketid,
 			&config);
-	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_fib_lookup_struct[socketid] == NULL) {
+		free(route_base_v4);
 		rte_exit(EXIT_FAILURE,
 				"Unable to create the l3fwd FIB table on socket %d\n",
 				socketid);
+	}
 
 	/* Populate the fib IPv6 table. */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* Skip unused ports. */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv6_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_fib6_add(ipv6_l3fwd_fib_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			free(route_base_v4);
+			free(route_base_v6);
 			rte_exit(EXIT_FAILURE,
 					"Unable to add entry %u to the l3fwd FIB table on socket %d\n",
 					i, socketid);
 		}
 
-		if (inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip,
+		if (inet_ntop(AF_INET6, route_base_v6[i].ip_8,
 				abuf, sizeof(abuf)) != NULL) {
 			printf("FIB: Adding route %s / %d (%d) [%s]\n", abuf,
-			       ipv6_l3fwd_route_array[i].depth,
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].depth,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		} else {
 			printf("FIB: IPv6 route added to port %d [%s]\n",
-			       ipv6_l3fwd_route_array[i].if_out,
+			       route_base_v6[i].if_out,
 			       dev_info.device->name);
 		}
 	}
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index a5b476ced3..bec22c44cd 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -30,7 +30,7 @@
 #include "l3fwd.h"
 #include "l3fwd_event.h"
 
-#include "l3fwd_route.h"
+#include "lpm_route_parse.c"
 
 #define IPV4_L3FWD_LPM_MAX_RULES         1024
 #define IPV4_L3FWD_LPM_NUMBER_TBL8S (1 << 8)
@@ -554,7 +554,7 @@ setup_lpm(const int socketid)
 	struct rte_eth_dev_info dev_info;
 	struct rte_lpm6_config config;
 	struct rte_lpm_config config_ipv4;
-	unsigned i;
+	int i;
 	int ret;
 	char s[64];
 	char abuf[INET6_ADDRSTRLEN];
@@ -572,32 +572,33 @@ setup_lpm(const int socketid)
 			socketid);
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv4_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v4; i++) {
 		struct in_addr in;
 
 		/* skip unused ports */
-		if ((1 << ipv4_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v4[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v4[i].if_out,
 				     &dev_info);
 		ret = rte_lpm_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
-			ipv4_l3fwd_route_array[i].ip,
-			ipv4_l3fwd_route_array[i].depth,
-			ipv4_l3fwd_route_array[i].if_out);
+			route_base_v4[i].ip,
+			route_base_v4[i].depth,
+			route_base_v4[i].if_out);
 
 		if (ret < 0) {
+			lpm_free_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
-		in.s_addr = htonl(ipv4_l3fwd_route_array[i].ip);
+		in.s_addr = htonl(route_base_v4[i].ip);
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
 		       inet_ntop(AF_INET, &in, abuf, sizeof(abuf)),
-		       ipv4_l3fwd_route_array[i].depth,
-		       ipv4_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v4[i].depth,
+		       route_base_v4[i].if_out, dev_info.device->name);
 	}
 
 	/* create the LPM6 table */
@@ -608,37 +609,40 @@ setup_lpm(const int socketid)
 	config.flags = 0;
 	ipv6_l3fwd_lpm_lookup_struct[socketid] = rte_lpm6_create(s, socketid,
 				&config);
-	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL)
+	if (ipv6_l3fwd_lpm_lookup_struct[socketid] == NULL) {
+		lpm_free_routes();
 		rte_exit(EXIT_FAILURE,
 			"Unable to create the l3fwd LPM table on socket %d\n",
 			socketid);
+	}
 
 	/* populate the LPM table */
-	for (i = 0; i < RTE_DIM(ipv6_l3fwd_route_array); i++) {
+	for (i = 0; i < route_num_v6; i++) {
 
 		/* skip unused ports */
-		if ((1 << ipv6_l3fwd_route_array[i].if_out &
+		if ((1 << route_base_v6[i].if_out &
 				enabled_port_mask) == 0)
 			continue;
 
-		rte_eth_dev_info_get(ipv4_l3fwd_route_array[i].if_out,
+		rte_eth_dev_info_get(route_base_v6[i].if_out,
 				     &dev_info);
 		ret = rte_lpm6_add(ipv6_l3fwd_lpm_lookup_struct[socketid],
-			ipv6_l3fwd_route_array[i].ip,
-			ipv6_l3fwd_route_array[i].depth,
-			ipv6_l3fwd_route_array[i].if_out);
+			route_base_v6[i].ip_8,
+			route_base_v6[i].depth,
+			route_base_v6[i].if_out);
 
 		if (ret < 0) {
+			lpm_free_routes();
 			rte_exit(EXIT_FAILURE,
 				"Unable to add entry %u to the l3fwd LPM table on socket %d\n",
 				i, socketid);
 		}
 
 		printf("LPM: Adding route %s / %d (%d) [%s]\n",
-		       inet_ntop(AF_INET6, ipv6_l3fwd_route_array[i].ip, abuf,
+		       inet_ntop(AF_INET6, route_base_v6[i].ip_8, abuf,
 				 sizeof(abuf)),
-		       ipv6_l3fwd_route_array[i].depth,
-		       ipv6_l3fwd_route_array[i].if_out, dev_info.device->name);
+		       route_base_v6[i].depth,
+		       route_base_v6[i].if_out, dev_info.device->name);
 	}
 }
 
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index c7eba06c4d..63c0b4dde5 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -2,6 +2,25 @@
  * Copyright(c) 2021 Intel Corporation
  */
 
+/*Log file related character defs. */
+#define COMMENT_LEAD_CHAR	('#')
+#define ROUTE_LEAD_CHAR		('R')
+
+#define	IPV6_ADDR_LEN	16
+#define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
+#define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
+	unsigned long val;                                      \
+	char *end;                                              \
+	errno = 0;                                              \
+	val = strtoul((in), &end, (base));                      \
+	if (errno != 0 || end[0] != (dlm) || val > (lim))       \
+		return -EINVAL;                               \
+	(fd) = (typeof(fd))val;                                 \
+	(in) = end + 1;                                         \
+} while (0)
+
 struct ipv4_l3fwd_route {
 	uint32_t ip;
 	uint8_t  depth;
@@ -14,6 +33,38 @@ struct ipv6_l3fwd_route {
 	uint8_t if_out;
 };
 
+struct lpm_route_rule {
+	union {
+		uint32_t ip;
+		union {
+			uint32_t ip_32[IPV6_ADDR_U32];
+			uint8_t ip_8[IPV6_ADDR_LEN];
+		};
+	};
+	uint8_t depth;
+	uint8_t if_out;
+};
+
+extern struct lpm_route_rule *route_base_v4;
+extern struct lpm_route_rule *route_base_v6;
+extern int route_num_v4;
+extern int route_num_v6;
+
 extern const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[16];
 
 extern const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[16];
+
+void
+read_config_files_lpm(void);
+
+void
+read_config_files_em(void);
+
+void
+em_free_routes(void);
+
+void
+lpm_free_routes(void);
+
+int
+is_bypass_line(const char *buff);
diff --git a/examples/l3fwd/lpm_default_v4.cfg b/examples/l3fwd/lpm_default_v4.cfg
new file mode 100644
index 0000000000..4db5597ed8
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD LPM
+R198.18.0.0/24 0
+R198.18.1.0/24 1
+R198.18.2.0/24 2
+R198.18.3.0/24 3
+R198.18.4.0/24 4
+R198.18.5.0/24 5
+R198.18.6.0/24 6
+R198.18.7.0/24 7
+R198.18.8.0/24 8
+R198.18.9.0/24 9
+R198.18.10.0/24 10
+R198.18.11.0/24 11
+R198.18.12.0/24 12
+R198.18.13.0/24 13
+R198.18.14.0/24 14
+R198.18.15.0/24 15
diff --git a/examples/l3fwd/lpm_default_v6.cfg b/examples/l3fwd/lpm_default_v6.cfg
new file mode 100644
index 0000000000..c50233e0ba
--- /dev/null
+++ b/examples/l3fwd/lpm_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD LPM
+R2001:0200:0000:0000:0000:0000:0000:0000/64 0
+R2001:0200:0000:0001:0000:0000:0000:0000/64 1
+R2001:0200:0000:0002:0000:0000:0000:0000/64 2
+R2001:0200:0000:0003:0000:0000:0000:0000/64 3
+R2001:0200:0000:0004:0000:0000:0000:0000/64 4
+R2001:0200:0000:0005:0000:0000:0000:0000/64 5
+R2001:0200:0000:0006:0000:0000:0000:0000/64 6
+R2001:0200:0000:0007:0000:0000:0000:0000/64 7
+R2001:0200:0000:0008:0000:0000:0000:0000/64 8
+R2001:0200:0000:0009:0000:0000:0000:0000/64 9
+R2001:0200:0000:000A:0000:0000:0000:0000/64 10
+R2001:0200:0000:000B:0000:0000:0000:0000/64 11
+R2001:0200:0000:000C:0000:0000:0000:0000/64 12
+R2001:0200:0000:000D:0000:0000:0000:0000/64 13
+R2001:0200:0000:000E:0000:0000:0000:0000/64 14
+R2001:0200:0000:000F:0000:0000:0000:0000/64 15
diff --git a/examples/l3fwd/lpm_route_parse.c b/examples/l3fwd/lpm_route_parse.c
new file mode 100644
index 0000000000..f2028d79e1
--- /dev/null
+++ b/examples/l3fwd/lpm_route_parse.c
@@ -0,0 +1,302 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "l3fwd.h"
+#include "l3fwd_route.h"
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
+
+struct lpm_route_rule *route_base_v4;
+struct lpm_route_rule *route_base_v6;
+int route_num_v4;
+int route_num_v6;
+
+/* Bypass comment and empty lines */
+int
+is_bypass_line(const char *buff)
+{
+	int i = 0;
+
+	/* comment line */
+	if (buff[0] == COMMENT_LEAD_CHAR)
+		return 1;
+	/* empty line */
+	while (buff[i] != '\0') {
+		if (!isspace(buff[i]))
+			return 0;
+		i++;
+	}
+	return 1;
+}
+
+static int
+parse_ipv6_addr_mask(char *token, uint32_t *ipv6, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET6, sa, ipv6) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 128, 0);
+	return 0;
+}
+
+static int
+parse_ipv4_addr_mask(char *token, uint32_t *ipv4, uint8_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(token, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET, sa, ipv4) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 32, 0);
+	*ipv4 = ntohl(*ipv4);
+	return 0;
+}
+
+static int
+lpm_parse_v6_net(char *in, uint32_t *v, uint8_t *mask_len)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = parse_ipv6_addr_mask(in, v, mask_len);
+
+	return rc;
+}
+
+static int
+lpm_parse_v6_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = lpm_parse_v6_net(in[CB_FLD_DST_ADDR], v->ip_32, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_parse_v4_rule(char *str, struct lpm_route_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = parse_ipv4_addr_mask(in[CB_FLD_DST_ADDR], &v->ip, &v->depth);
+
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return rc;
+}
+
+static int
+lpm_add_default_v4_rules(void)
+{
+	/* populate the LPM IPv4 table */
+	unsigned int i, rule_size = sizeof(*route_base_v4);
+	route_num_v4 = RTE_DIM(ipv4_l3fwd_route_array);
+
+	route_base_v4 = calloc(route_num_v4, rule_size);
+
+	for (i = 0; i < (unsigned int)route_num_v4; i++) {
+		route_base_v4[i].ip = ipv4_l3fwd_route_array[i].ip;
+		route_base_v4[i].depth = ipv4_l3fwd_route_array[i].depth;
+		route_base_v4[i].if_out = ipv4_l3fwd_route_array[i].if_out;
+	}
+	return 0;
+}
+
+static int
+lpm_add_default_v6_rules(void)
+{
+	/* populate the LPM IPv6 table */
+	unsigned int i, rule_size = sizeof(*route_base_v6);
+	route_num_v6 = RTE_DIM(ipv6_l3fwd_route_array);
+
+	route_base_v6 = calloc(route_num_v6, rule_size);
+
+	for (i = 0; i < (unsigned int)route_num_v6; i++) {
+		memcpy(route_base_v6[i].ip_8, ipv6_l3fwd_route_array[i].ip,
+			   sizeof(route_base_v6[i].ip_8));
+		route_base_v6[i].depth = ipv6_l3fwd_route_array[i].depth;
+		route_base_v6[i].if_out = ipv6_l3fwd_route_array[i].if_out;
+	}
+	return 0;
+}
+
+static int
+lpm_add_rules(const char *rule_path,
+		struct lpm_route_rule **proute_base,
+		int (*parser)(char *, struct lpm_route_rule *))
+{
+	struct lpm_route_rule *route_rules;
+	struct lpm_route_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
+void
+lpm_free_routes(void)
+{
+	free(route_base_v4);
+	free(route_base_v6);
+	route_base_v4 = NULL;
+	route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
+}
+
+/* Load rules from the input file */
+void
+read_config_files_lpm(void)
+{
+	if (parm_config.rule_ipv4_name != NULL &&
+			parm_config.rule_ipv6_name != NULL) {
+		/* ipv4 check */
+		route_num_v4 = lpm_add_rules(parm_config.rule_ipv4_name,
+					&route_base_v4, &lpm_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			lpm_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv4 rules\n");
+		}
+
+		/* ipv6 check */
+		route_num_v6 = lpm_add_rules(parm_config.rule_ipv6_name,
+					&route_base_v6, &lpm_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			lpm_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(INFO, L3FWD, "Missing 1 or more rule files, using default instead\n");
+		if (lpm_add_default_v4_rules() < 0) {
+			lpm_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add default IPv4 rules\n");
+		}
+		if (lpm_add_default_v6_rules() < 0) {
+			lpm_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add default IPv6 rules\n");
+		}
+	}
+}
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index eb68ffc5aa..05652a560a 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -94,6 +94,8 @@ uint32_t hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 
 struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
+struct parm_cfg parm_config;
+
 struct lcore_params {
 	uint16_t port_id;
 	uint8_t queue_id;
@@ -141,41 +143,49 @@ static struct rte_mempool *vector_pool[RTE_MAX_ETHPORTS];
 static uint8_t lkp_per_socket[NB_SOCKETS];
 
 struct l3fwd_lkp_mode {
+	void  (*read_config_files)(void);
 	void  (*setup)(int);
 	int   (*check_ptype)(int);
 	rte_rx_callback_fn cb_parse_ptype;
 	int   (*main_loop)(void *);
 	void* (*get_ipv4_lookup_struct)(int);
 	void* (*get_ipv6_lookup_struct)(int);
+	void  (*free_routes)(void);
 };
 
 static struct l3fwd_lkp_mode l3fwd_lkp;
 
 static struct l3fwd_lkp_mode l3fwd_em_lkp = {
+	.read_config_files		= read_config_files_em,
 	.setup                  = setup_hash,
 	.check_ptype		= em_check_ptype,
 	.cb_parse_ptype		= em_cb_parse_ptype,
 	.main_loop              = em_main_loop,
 	.get_ipv4_lookup_struct = em_get_ipv4_l3fwd_lookup_struct,
 	.get_ipv6_lookup_struct = em_get_ipv6_l3fwd_lookup_struct,
+	.free_routes			= em_free_routes,
 };
 
 static struct l3fwd_lkp_mode l3fwd_lpm_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_lpm,
 	.check_ptype		= lpm_check_ptype,
 	.cb_parse_ptype		= lpm_cb_parse_ptype,
 	.main_loop              = lpm_main_loop,
 	.get_ipv4_lookup_struct = lpm_get_ipv4_l3fwd_lookup_struct,
 	.get_ipv6_lookup_struct = lpm_get_ipv6_l3fwd_lookup_struct,
+	.free_routes			= lpm_free_routes,
 };
 
 static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
+	.read_config_files		= read_config_files_lpm,
 	.setup                  = setup_fib,
 	.check_ptype            = lpm_check_ptype,
 	.cb_parse_ptype         = lpm_cb_parse_ptype,
 	.main_loop              = fib_main_loop,
 	.get_ipv4_lookup_struct = fib_get_ipv4_l3fwd_lookup_struct,
 	.get_ipv6_lookup_struct = fib_get_ipv6_l3fwd_lookup_struct,
+	.free_routes			= lpm_free_routes,
 };
 
 /*
@@ -224,6 +234,21 @@ const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[] = {
 	{{32, 1, 2, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0}, 64, 15},
 };
 
+/*
+ * API's called during initialization to setup ACL/EM/LPM rules.
+ */
+static void
+l3fwd_set_rule_ipv4_name(const char *optarg)
+{
+	parm_config.rule_ipv4_name = optarg;
+}
+
+static void
+l3fwd_set_rule_ipv6_name(const char *optarg)
+{
+	parm_config.rule_ipv6_name = optarg;
+}
+
 /*
  * Setup lookup methods for forwarding.
  * Currently exact-match, longest-prefix-match and forwarding information
@@ -339,6 +364,8 @@ print_usage(const char *prgname)
 {
 	fprintf(stderr, "%s [EAL options] --"
 		" -p PORTMASK"
+		"  --rule_ipv4=FILE"
+		"  --rule_ipv6=FILE"
 		" [-P]"
 		" [--lookup]"
 		" --config (port,queue,lcore)[,(port,queue,lcore)]"
@@ -381,7 +408,10 @@ print_usage(const char *prgname)
 		"  --event-vector-size: Max vector size if event vectorization is enabled.\n"
 		"  --event-vector-tmo: Max timeout to form vector in nanoseconds if event vectorization is enabled\n"
 		"  -E : Enable exact match, legacy flag please use --lookup=em instead\n"
-		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n\n",
+		"  -L : Enable longest prefix match, legacy flag please use --lookup=lpm instead\n"
+		"  --rule_ipv4=FILE: Specify the ipv4 rules entries file.\n"
+		"                    Each rule occupies one line.\n"
+		"  --rule_ipv6=FILE: Specify the ipv6 rules entries file.\n\n",
 		prgname);
 }
 
@@ -596,6 +626,8 @@ static const char short_options[] =
 #define CMD_LINE_OPT_ENABLE_VECTOR "event-vector"
 #define CMD_LINE_OPT_VECTOR_SIZE "event-vector-size"
 #define CMD_LINE_OPT_VECTOR_TMO_NS "event-vector-tmo"
+#define CMD_LINE_OPT_RULE_IPV4 "rule_ipv4"
+#define CMD_LINE_OPT_RULE_IPV6 "rule_ipv6"
 
 enum {
 	/* long options mapped to a short option */
@@ -610,6 +642,8 @@ enum {
 	CMD_LINE_OPT_MAX_PKT_LEN_NUM,
 	CMD_LINE_OPT_HASH_ENTRY_NUM_NUM,
 	CMD_LINE_OPT_PARSE_PTYPE_NUM,
+	CMD_LINE_OPT_RULE_IPV4_NUM,
+	CMD_LINE_OPT_RULE_IPV6_NUM,
 	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
 	CMD_LINE_OPT_MODE_NUM,
 	CMD_LINE_OPT_EVENTQ_SYNC_NUM,
@@ -637,6 +671,8 @@ static const struct option lgopts[] = {
 	{CMD_LINE_OPT_ENABLE_VECTOR, 0, 0, CMD_LINE_OPT_ENABLE_VECTOR_NUM},
 	{CMD_LINE_OPT_VECTOR_SIZE, 1, 0, CMD_LINE_OPT_VECTOR_SIZE_NUM},
 	{CMD_LINE_OPT_VECTOR_TMO_NS, 1, 0, CMD_LINE_OPT_VECTOR_TMO_NS_NUM},
+	{CMD_LINE_OPT_RULE_IPV4,   1, 0, CMD_LINE_OPT_RULE_IPV4_NUM},
+	{CMD_LINE_OPT_RULE_IPV6,   1, 0, CMD_LINE_OPT_RULE_IPV6_NUM},
 	{NULL, 0, 0, 0}
 };
 
@@ -791,6 +827,12 @@ parse_args(int argc, char **argv)
 		case CMD_LINE_OPT_VECTOR_TMO_NS_NUM:
 			evt_rsrc->vector_tmo_ns = strtoull(optarg, NULL, 10);
 			break;
+		case CMD_LINE_OPT_RULE_IPV4_NUM:
+			l3fwd_set_rule_ipv4_name(optarg);
+			break;
+		case CMD_LINE_OPT_RULE_IPV6_NUM:
+			l3fwd_set_rule_ipv6_name(optarg);
+			break;
 		default:
 			print_usage(prgname);
 			return -1;
@@ -1395,6 +1437,9 @@ main(int argc, char **argv)
 	/* Setup function pointers for lookup method. */
 	setup_l3fwd_lookup_tables();
 
+	/* Add the config file rules */
+	l3fwd_lkp.read_config_files();
+
 	evt_rsrc->per_port_pool = per_port_pool;
 	evt_rsrc->pkt_pool = pktmbuf_pool;
 	evt_rsrc->vec_pool = vector_pool;
@@ -1501,6 +1546,9 @@ main(int argc, char **argv)
 		}
 	}
 
+	/* clean up config file routes */
+	l3fwd_lkp.free_routes();
+
 	/* clean up the EAL */
 	rte_eal_cleanup();
 
-- 
2.25.1


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

* [PATCH v6 2/2] examples/l3fwd: add config file support for EM
  2022-03-01 14:49         ` [PATCH v6 " Sean Morrissey
  2022-03-01 14:49           ` [PATCH v6 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
@ 2022-03-01 14:49           ` Sean Morrissey
  2022-03-08  8:57           ` [PATCH v6 0/2] Add config file support for l3fwd Thomas Monjalon
  2 siblings, 0 replies; 46+ messages in thread
From: Sean Morrissey @ 2022-03-01 14:49 UTC (permalink / raw)
  Cc: dev, Sean Morrissey, Konstantin Ananyev

Add support to define ipv4 and ipv6 forwarding tables
from reading from a config file for EM with a format
similar to l3fwd-acl one.

Users can now use the default hardcoded route tables
or optionally config files for 'l3fwd_em'. Default
config files have been provided for use with EM.

Related l3fwd docs have been updated to relfect these
changes.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 doc/guides/sample_app_ug/l3_forward.rst |  89 ++++++--
 examples/l3fwd/em_default_v4.cfg        |  17 ++
 examples/l3fwd/em_default_v6.cfg        |  17 ++
 examples/l3fwd/em_route_parse.c         | 266 +++++++++++++++++++++++-
 examples/l3fwd/l3fwd_em.c               | 219 +++++++------------
 examples/l3fwd/l3fwd_route.h            |  38 +++-
 6 files changed, 479 insertions(+), 167 deletions(-)
 create mode 100644 examples/l3fwd/em_default_v4.cfg
 create mode 100644 examples/l3fwd/em_default_v6.cfg

diff --git a/doc/guides/sample_app_ug/l3_forward.rst b/doc/guides/sample_app_ug/l3_forward.rst
index 6d7d7c5cc1..01d86db95d 100644
--- a/doc/guides/sample_app_ug/l3_forward.rst
+++ b/doc/guides/sample_app_ug/l3_forward.rst
@@ -47,6 +47,7 @@ and loaded into the LPM or FIB object at initialization time.
 In the sample application, hash-based and FIB-based forwarding supports
 both IPv4 and IPv6.
 LPM-based forwarding supports IPv4 only.
+During the initialization phase route rules for IPv4 and IPv6 are read from rule files.
 
 Compiling the Application
 -------------------------
@@ -61,6 +62,8 @@ Running the Application
 The application has a number of command line options::
 
     ./dpdk-l3fwd [EAL options] -- -p PORTMASK
+                             --rule_ipv4=FILE
+                             --rule_ipv6=FILE
                              [-P]
                              [--lookup LOOKUP_METHOD]
                              --config(port,queue,lcore)[,(port,queue,lcore)]
@@ -82,6 +85,11 @@ Where,
 
 * ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
 
+* ``--rule_ipv4=FILE:`` specify the ipv4 rules entries file.
+  Each rule occupies one line.
+
+* ``--rule_ipv6=FILE:`` specify the ipv6 rules entries file.
+
 * ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
   Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
 
@@ -135,7 +143,7 @@ To enable L3 forwarding between two ports, assuming that both ports are in the s
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
+    ./<build_dir>/examples/dpdk-l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)" --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -157,19 +165,23 @@ In this command:
 |          |           |           |                                     |
 +----------+-----------+-----------+-------------------------------------+
 
+*   The -rule_ipv4 option specifies the reading of IPv4 rules sets from the rule_ipv4.cfg file
+
+*   The -rule_ipv6 option specifies the reading of IPv6 rules sets from the rule_ipv6.cfg file.
+
 To use eventdev mode with sync method **ordered** on above mentioned environment,
 Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> -- -p 0x3 --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 or
 
 .. code-block:: console
 
     ./<build_dir>/examples/dpdk-l3fwd -l 0-3 -n 4 -a <event device> \
-		-- -p 0x03 --mode=eventdev --eventq-sched=ordered
+		-- -p 0x03 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In this command:
 
@@ -192,7 +204,7 @@ scheduler. Following is the sample command:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered
+    ./<build_dir>/examples/dpdk-l3fwd -l 0-7 -s 0xf0000 -n 4 --vdev event_sw0 -- -p 0x3 --mode=eventdev --eventq-sched=ordered --rule_ipv4="rule_ipv4.cfg" --rule_ipv6="rule_ipv6.cfg"
 
 In case of eventdev mode, *--config* option is not used for ethernet port
 configuration. Instead each ethernet port will be configured with mentioned
@@ -216,6 +228,49 @@ The following sections provide some explanation of the sample application code.
 the initialization and run-time paths are very similar to those of the :doc:`l2_forward_real_virtual` and :doc:`l2_forward_event`.
 The following sections describe aspects that are specific to the L3 Forwarding sample application.
 
+Parse Rules from File
+~~~~~~~~~~~~~~~~~~~~~
+
+The application parses the rules from the file and adds them to the appropriate route table by calling the appropriate function.
+It ignores empty and comment lines, and parses and validates the rules it reads.
+If errors are detected, the application exits with messages to identify the errors encountered.
+
+The format of the route rules differs based on which lookup method is being used.
+Therefore, the code only decreases the priority number with each rule it parses.
+Route rules are mandatory.
+To read data from the specified file successfully, the application assumes the following:
+
+*   Each rule occupies a single line.
+
+*   Only the following four rule line types are valid in this application:
+
+*   Route rule line, which starts with a leading character 'R'
+
+*   Comment line, which starts with a leading character '#'
+
+*   Empty line, which consists of a space, form-feed ('\f'), newline ('\n'),
+    carriage return ('\r'), horizontal tab ('\t'), or vertical tab ('\v').
+
+Other lines types are considered invalid.
+
+*   Rules are organized in descending order of priority,
+    which means rules at the head of the file always have a higher priority than those further down in the file.
+
+*   A typical IPv4 LPM/FIB rule line should have a format as shown below:
+
+R<destination_ip>/<ip_mask_length><output_port_number>
+
+*   A typical IPv4 EM rule line should have a format as shown below:
+
+R<destination_ip><source_ip><destination_port><source_port><protocol><output_port_number>
+
+IPv4 addresses are specified in CIDR format as specified in RFC 4632.
+For LPM/FIB they consist of the dot notation for the address and a prefix length separated by '/'.
+For example, 192.168.0.34/32, where the address is 192.168.0.34 and the prefix length is 32.
+For EM they consist of just the dot notation for the address and no prefix length.
+For example, 192.168.0.34, where the Address is 192.168.0.34.
+EM also includes ports which are specified as a single number which represents a single port.
+
 Hash Initialization
 ~~~~~~~~~~~~~~~~~~~
 
@@ -227,8 +282,6 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
 
     The Hash initialization will setup both ipv4 and ipv6 hash table,
     and populate the either table depending on the value of variable ipv6.
-    To support the hash performance test with up to 8M single direction flows/16M bi-direction flows,
-    populate_ipv4_many_flow_into_table() function will populate the hash table with specified hash table entry number(default 4M).
 
 .. note::
 
@@ -246,22 +299,14 @@ for the convenience to execute hash performance test on 4M/8M/16M flows.
         {
             // ...
 
-            if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_many_flow_into_table(ipv4_l3fwd_lookup_struct[socketid], hash_entry_number);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_many_flow_into_table( ipv6_l3fwd_lookup_struct[socketid], hash_entry_number);
-                }
-            } else
-                if (ipv6 == 0) {
-                    /* populate the ipv4 hash */
-                    populate_ipv4_few_flow_into_table(ipv4_l3fwd_lookup_struct[socketid]);
-                } else {
-                    /* populate the ipv6 hash */
-                    populate_ipv6_few_flow_into_table(ipv6_l3fwd_lookup_struct[socketid]);
-                }
+            if (ipv6 == 0) {
+                /* populate the ipv4 hash */
+                populate_ipv4_flow_into_table(
+                    ipv4_l3fwd_em_lookup_struct[socketid]);
+            } else {
+                /* populate the ipv6 hash */
+                populate_ipv6_flow_into_table(
+                    ipv6_l3fwd_em_lookup_struct[socketid]);
             }
         }
     #endif
diff --git a/examples/l3fwd/em_default_v4.cfg b/examples/l3fwd/em_default_v4.cfg
new file mode 100644
index 0000000000..b70bdb6601
--- /dev/null
+++ b/examples/l3fwd/em_default_v4.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv4 FWD table for L3FWD EM
+R198.18.0.0 198.18.0.1 9 9 0x11 0
+R198.18.1.0 198.18.1.1 9 9 0x11 1
+R198.18.2.0 198.18.2.1 9 9 0x11 2
+R198.18.3.0 198.18.3.1 9 9 0x11 3
+R198.18.4.0 198.18.4.1 9 9 0x11 4
+R198.18.5.0 198.18.5.1 9 9 0x11 5
+R198.18.6.0 198.18.6.1 9 9 0x11 6
+R198.18.7.0 198.18.7.1 9 9 0x11 7
+R198.18.8.0 198.18.8.1 9 9 0x11 8
+R198.18.9.0 198.18.9.1 9 9 0x11 9
+R198.18.10.0 198.18.10.1 9 9 0x11 10
+R198.18.11.0 198.18.11.1 9 9 0x11 11
+R198.18.12.0 198.18.12.1 9 9 0x11 12
+R198.18.13.0 198.18.13.1 9 9 0x11 13
+R198.18.14.0 198.18.14.1 9 9 0x11 14
+R198.18.15.0 198.18.15.1 9 9 0x11 15
diff --git a/examples/l3fwd/em_default_v6.cfg b/examples/l3fwd/em_default_v6.cfg
new file mode 100644
index 0000000000..0b94f8d8d2
--- /dev/null
+++ b/examples/l3fwd/em_default_v6.cfg
@@ -0,0 +1,17 @@
+#Copy of hard-coded IPv6 FWD table for L3FWD EM
+R2001:0200:0000:0000:0000:0000:0000:0000 2001:0200:0000:0000:0000:0000:0000:0001 9 9 0x11 0
+R2001:0200:0000:0001:0000:0000:0000:0000 2001:0200:0000:0001:0000:0000:0000:0001 9 9 0x11 1
+R2001:0200:0000:0002:0000:0000:0000:0000 2001:0200:0000:0002:0000:0000:0000:0001 9 9 0x11 2
+R2001:0200:0000:0003:0000:0000:0000:0000 2001:0200:0000:0003:0000:0000:0000:0001 9 9 0x11 3
+R2001:0200:0000:0004:0000:0000:0000:0000 2001:0200:0000:0004:0000:0000:0000:0001 9 9 0x11 4
+R2001:0200:0000:0005:0000:0000:0000:0000 2001:0200:0000:0005:0000:0000:0000:0001 9 9 0x11 5
+R2001:0200:0000:0006:0000:0000:0000:0000 2001:0200:0000:0006:0000:0000:0000:0001 9 9 0x11 6
+R2001:0200:0000:0007:0000:0000:0000:0000 2001:0200:0000:0007:0000:0000:0000:0001 9 9 0x11 7
+R2001:0200:0000:0008:0000:0000:0000:0000 2001:0200:0000:0008:0000:0000:0000:0001 9 9 0x11 8
+R2001:0200:0000:0009:0000:0000:0000:0000 2001:0200:0000:0009:0000:0000:0000:0001 9 9 0x11 9
+R2001:0200:0000:000A:0000:0000:0000:0000 2001:0200:0000:000A:0000:0000:0000:0001 9 9 0x11 10
+R2001:0200:0000:000B:0000:0000:0000:0000 2001:0200:0000:000B:0000:0000:0000:0001 9 9 0x11 11
+R2001:0200:0000:000C:0000:0000:0000:0000 2001:0200:0000:000C:0000:0000:0000:0001 9 9 0x11 12
+R2001:0200:0000:000D:0000:0000:0000:0000 2001:0200:0000:000D:0000:0000:0000:0001 9 9 0x11 13
+R2001:0200:0000:000E:0000:0000:0000:0000 2001:0200:0000:000E:0000:0000:0000:0001 9 9 0x11 14
+R2001:0200:0000:000F:0000:0000:0000:0000 2001:0200:0000:000F:0000:0000:0000:0001 9 9 0x11 15
diff --git a/examples/l3fwd/em_route_parse.c b/examples/l3fwd/em_route_parse.c
index 11d83d9b42..6c16832e94 100644
--- a/examples/l3fwd/em_route_parse.c
+++ b/examples/l3fwd/em_route_parse.c
@@ -2,17 +2,279 @@
  * Copyright(c) 2022 Intel Corporation
  */
 
+#include <stdio.h>
+#include <stdint.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "l3fwd.h"
 #include "l3fwd_route.h"
 
+static struct em_rule *em_route_base_v4;
+static struct em_rule *em_route_base_v6;
+
+enum {
+	CB_FLD_DST_ADDR,
+	CB_FLD_SRC_ADDR,
+	CB_FLD_DST_PORT,
+	CB_FLD_SRC_PORT,
+	CB_FLD_PROTO,
+	CB_FLD_IF_OUT,
+	CB_FLD_MAX
+};
+
+static int
+em_parse_v6_net(const char *in, uint8_t *v)
+{
+	int32_t rc;
+
+	/* get address. */
+	rc = inet_pton(AF_INET6, in, v);
+	if (rc != 1)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+em_parse_v6_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = em_parse_v6_net(in[CB_FLD_DST_ADDR], v->v6_key.ip_dst);
+	if (rc != 0)
+		return rc;
+	rc = em_parse_v6_net(in[CB_FLD_SRC_ADDR], v->v6_key.ip_src);
+	if (rc != 0)
+		return rc;
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v6_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v6_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v6_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_parse_v4_rule(char *str, struct em_rule *v)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_MAX];
+	static const char *dlm = " \t\n";
+	int dim = CB_FLD_MAX;
+	s = str;
+
+	for (i = 0; i != dim; i++, s = NULL) {
+		in[i] = strtok_r(s, dlm, &sp);
+		if (in[i] == NULL)
+			return -EINVAL;
+	}
+
+	rc = inet_pton(AF_INET, in[CB_FLD_DST_ADDR], &(v->v4_key.ip_dst));
+	v->v4_key.ip_dst = ntohl(v->v4_key.ip_dst);
+	if (rc != 1)
+		return rc;
+
+	rc = inet_pton(AF_INET, in[CB_FLD_SRC_ADDR], &(v->v4_key.ip_src));
+	v->v4_key.ip_src = ntohl(v->v4_key.ip_src);
+	if (rc != 1)
+		return rc;
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT], v->v4_key.port_src, 0, UINT16_MAX, 0);
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT], v->v4_key.port_dst, 0, UINT16_MAX, 0);
+	/* protocol. */
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->v4_key.proto, 0, UINT8_MAX, 0);
+	/* out interface. */
+	GET_CB_FIELD(in[CB_FLD_IF_OUT], v->if_out, 0, UINT8_MAX, 0);
+
+	return 0;
+}
+
+static int
+em_add_rules(const char *rule_path,
+		struct em_rule **proute_base,
+		int (*parser)(char *, struct em_rule *))
+{
+	struct em_rule *route_rules;
+	struct em_rule *next;
+	unsigned int route_num = 0;
+	unsigned int route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh;
+	unsigned int i = 0, rule_size = sizeof(*next);
+	int val;
+
+	*proute_base = NULL;
+	fh = fopen(rule_path, "rb");
+	if (fh == NULL)
+		return -EINVAL;
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+	}
+
+	if (route_num == 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL) {
+		fclose(fh);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (fgets(buff, LINE_MAX, fh) != NULL) {
+		i++;
+		if (is_bypass_line(buff))
+			continue;
+
+		char s = buff[0];
+
+		/* Route entry */
+		if (s == ROUTE_LEAD_CHAR)
+			next = &route_rules[route_cnt];
+
+		/* Illegal line */
+		else {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: should start with leading "
+				"char %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		if (parser(buff + 1, next) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+			fclose(fh);
+			free(route_rules);
+			return -EINVAL;
+		}
+
+		route_cnt++;
+	}
+
+	fclose(fh);
+
+	*proute_base = route_rules;
+
+	return route_cnt;
+}
+
+static int
+em_add_default_v4_rules(void)
+{
+	/* populate the LPM IPv4 table */
+	unsigned int i, rule_size = sizeof(*em_route_base_v4);
+	route_num_v4 = RTE_DIM(ipv4_l3fwd_em_route_array);
+
+	em_route_base_v4 = calloc(route_num_v4, rule_size);
+
+	for (i = 0; i < (unsigned int)route_num_v4; i++) {
+		em_route_base_v4[i].v4_key.ip_dst = ipv4_l3fwd_em_route_array[i].key.ip_dst;
+		em_route_base_v4[i].v4_key.ip_src = ipv4_l3fwd_em_route_array[i].key.ip_src;
+		em_route_base_v4[i].v4_key.port_dst = ipv4_l3fwd_em_route_array[i].key.port_dst;
+		em_route_base_v4[i].v4_key.port_src = ipv4_l3fwd_em_route_array[i].key.port_src;
+		em_route_base_v4[i].v4_key.proto = ipv4_l3fwd_em_route_array[i].key.proto;
+		em_route_base_v4[i].if_out = ipv4_l3fwd_em_route_array[i].if_out;
+	}
+	return 0;
+}
+
+static int
+em_add_default_v6_rules(void)
+{
+	/* populate the LPM IPv6 table */
+	unsigned int i, rule_size = sizeof(*em_route_base_v6);
+	route_num_v6 = RTE_DIM(ipv6_l3fwd_em_route_array);
+
+	em_route_base_v6 = calloc(route_num_v6, rule_size);
+
+	for (i = 0; i < (unsigned int)route_num_v6; i++) {
+		memcpy(em_route_base_v6[i].v6_key.ip_dst, ipv6_l3fwd_em_route_array[i].key.ip_dst,
+			   sizeof(em_route_base_v6[i].v6_key.ip_dst));
+		memcpy(em_route_base_v6[i].v6_key.ip_src, ipv6_l3fwd_em_route_array[i].key.ip_src,
+			   sizeof(em_route_base_v6[i].v6_key.ip_src));
+		em_route_base_v6[i].v6_key.port_dst = ipv6_l3fwd_em_route_array[i].key.port_dst;
+		em_route_base_v6[i].v6_key.port_src = ipv6_l3fwd_em_route_array[i].key.port_src;
+		em_route_base_v6[i].v6_key.proto = ipv6_l3fwd_em_route_array[i].key.proto;
+		em_route_base_v6[i].if_out = ipv6_l3fwd_em_route_array[i].if_out;
+	}
+	return 0;
+}
+
 void
 em_free_routes(void)
 {
-	/* Empty till config file support added to EM */
+	free(em_route_base_v4);
+	free(em_route_base_v6);
+	em_route_base_v4 = NULL;
+	em_route_base_v6 = NULL;
+	route_num_v4 = 0;
+	route_num_v6 = 0;
 }
 
 /* Load rules from the input file */
 void
 read_config_files_em(void)
 {
-	/* Empty till config file support added to EM */
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL &&
+			parm_config.rule_ipv6_name != NULL) {
+		/* ipv4 check */
+		route_num_v4 = em_add_rules(parm_config.rule_ipv4_name,
+					&em_route_base_v4, &em_parse_v4_rule);
+		if (route_num_v4 < 0) {
+			em_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv4 rules\n");
+		}
+
+		/* ipv6 check */
+		route_num_v6 = em_add_rules(parm_config.rule_ipv6_name,
+					&em_route_base_v6, &em_parse_v6_rule);
+		if (route_num_v6 < 0) {
+			em_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add EM IPv6 rules\n");
+		}
+	} else {
+		RTE_LOG(INFO, L3FWD, "Missing 1 or more rule files, using default instead\n");
+		if (em_add_default_v4_rules() < 0) {
+			em_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add default IPv4 rules\n");
+		}
+		if (em_add_default_v6_rules() < 0) {
+			em_free_routes();
+			rte_exit(EXIT_FAILURE, "Failed to add default IPv6 rules\n");
+		}
+	}
 }
diff --git a/examples/l3fwd/l3fwd_em.c b/examples/l3fwd/l3fwd_em.c
index de5be9e0a4..24d0910fe0 100644
--- a/examples/l3fwd/l3fwd_em.c
+++ b/examples/l3fwd/l3fwd_em.c
@@ -43,14 +43,6 @@
 
 #define IPV6_ADDR_LEN 16
 
-struct ipv4_5tuple {
-	uint32_t ip_dst;
-	uint32_t ip_src;
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
-
 union ipv4_5tuple_host {
 	struct {
 		uint8_t  pad0;
@@ -66,14 +58,6 @@ union ipv4_5tuple_host {
 
 #define XMM_NUM_IN_IPV6_5TUPLE 3
 
-struct ipv6_5tuple {
-	uint8_t  ip_dst[IPV6_ADDR_LEN];
-	uint8_t  ip_src[IPV6_ADDR_LEN];
-	uint16_t port_dst;
-	uint16_t port_src;
-	uint8_t  proto;
-} __rte_packed;
-
 union ipv6_5tuple_host {
 	struct {
 		uint16_t pad0;
@@ -88,22 +72,10 @@ union ipv6_5tuple_host {
 	xmm_t xmm[XMM_NUM_IN_IPV6_5TUPLE];
 };
 
-
-
-struct ipv4_l3fwd_em_route {
-	struct ipv4_5tuple key;
-	uint8_t if_out;
-};
-
-struct ipv6_l3fwd_em_route {
-	struct ipv6_5tuple key;
-	uint8_t if_out;
-};
-
 /* 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
  * Use RFC863 Discard Protocol.
  */
-static const struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[] = {
+const struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[] = {
 	{{RTE_IPV4(198, 18, 0, 0), RTE_IPV4(198, 18, 0, 1),  9, 9, IPPROTO_UDP}, 0},
 	{{RTE_IPV4(198, 18, 1, 0), RTE_IPV4(198, 18, 1, 1),  9, 9, IPPROTO_UDP}, 1},
 	{{RTE_IPV4(198, 18, 2, 0), RTE_IPV4(198, 18, 2, 1),  9, 9, IPPROTO_UDP}, 2},
@@ -125,7 +97,7 @@ static const struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[] = {
 /* 2001:0200::/48 is IANA reserved range for IPv6 benchmarking (RFC5180).
  * Use RFC863 Discard Protocol.
  */
-static const struct ipv6_l3fwd_em_route ipv6_l3fwd_em_route_array[] = {
+const struct ipv6_l3fwd_em_route ipv6_l3fwd_em_route_array[] = {
 	{{{32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
 	  {32, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 9, 9, IPPROTO_UDP}, 0},
 	{{{32, 1, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
@@ -236,10 +208,6 @@ ipv6_hash_crc(const void *data, __rte_unused uint32_t data_len,
 	return init_val;
 }
 
-#define IPV4_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv4_l3fwd_em_route_array)
-
-#define IPV6_L3FWD_EM_NUM_ROUTES RTE_DIM(ipv6_l3fwd_em_route_array)
-
 static uint8_t ipv4_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 static uint8_t ipv6_l3fwd_out_if[L3FWD_HASH_ENTRIES] __rte_cache_aligned;
 
@@ -383,122 +351,106 @@ convert_ipv6_5tuple(struct ipv6_5tuple *key1,
 #define BIT_8_TO_15 0x0000ff00
 
 static inline void
-populate_ipv4_few_flow_into_table(const struct rte_hash *h)
+populate_ipv4_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
-	for (i = 0; i < IPV4_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv4_l3fwd_em_route  entry;
+	for (i = 0; i < route_num_v4; i++) {
+		struct em_rule *entry;
 		union ipv4_5tuple_host newkey;
+		struct in_addr src;
+		struct in_addr dst;
 
-		entry = ipv4_l3fwd_em_route_array[i];
-		convert_ipv4_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v4[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v4[i];
+		convert_ipv4_5tuple(&(entry->v4_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv4_l3fwd_out_if[ret] = entry.if_out;
+		ipv4_l3fwd_out_if[ret] = entry->if_out;
+		ret = rte_eth_dev_info_get(em_route_base_v4[i].if_out,
+				     &dev_info);
+		if (ret != 0)
+			rte_exit(EXIT_FAILURE,
+				"Error during getting device (port %u) info: %s\n",
+				em_route_base_v4[i].if_out, strerror(-ret));
+
+		src.s_addr = htonl(em_route_base_v4[i].v4_key.ip_src);
+		dst.s_addr = htonl(em_route_base_v4[i].v4_key.ip_dst);
+		printf("EM: Adding route %s, %s, %d, %d, %d (%d) [%s]\n",
+			   inet_ntop(AF_INET, &dst, dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET, &src, srcbuf, sizeof(srcbuf)),
+			   em_route_base_v4[i].v4_key.port_dst,
+			   em_route_base_v4[i].v4_key.port_src,
+			   em_route_base_v4[i].v4_key.proto,
+		       em_route_base_v4[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 " keys\n",
-		(uint64_t)IPV4_L3FWD_EM_NUM_ROUTES);
+		(uint64_t)route_num_v4);
 }
 
 #define BIT_16_TO_23 0x00ff0000
 static inline void
-populate_ipv6_few_flow_into_table(const struct rte_hash *h)
+populate_ipv6_flow_into_table(const struct rte_hash *h)
 {
-	uint32_t i;
+	int i;
 	int32_t ret;
+	struct rte_eth_dev_info dev_info;
+	char srcbuf[INET6_ADDRSTRLEN];
+	char dstbuf[INET6_ADDRSTRLEN];
 
 	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
 				ALL_32_BITS, ALL_32_BITS} };
 
 	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
 
-	for (i = 0; i < IPV6_L3FWD_EM_NUM_ROUTES; i++) {
-		struct ipv6_l3fwd_em_route entry;
+	for (i = 0; i < route_num_v6; i++) {
+		struct em_rule *entry;
 		union ipv6_5tuple_host newkey;
 
-		entry = ipv6_l3fwd_em_route_array[i];
-		convert_ipv6_5tuple(&entry.key, &newkey);
+		if ((1 << em_route_base_v6[i].if_out &
+				enabled_port_mask) == 0)
+			continue;
+
+		entry = &em_route_base_v6[i];
+		convert_ipv6_5tuple(&(entry->v6_key), &newkey);
 		ret = rte_hash_add_key(h, (void *) &newkey);
 		if (ret < 0) {
 			rte_exit(EXIT_FAILURE, "Unable to add entry %" PRIu32
 				" to the l3fwd hash.\n", i);
 		}
-		ipv6_l3fwd_out_if[ret] = entry.if_out;
+		ipv6_l3fwd_out_if[ret] = entry->if_out;
+		ret = rte_eth_dev_info_get(em_route_base_v6[i].if_out,
+				     &dev_info);
+		if (ret != 0)
+			rte_exit(EXIT_FAILURE,
+				"Error during getting device (port %u) info: %s\n",
+				em_route_base_v6[i].if_out, strerror(-ret));
+
+		printf("EM: Adding route %s, %s, %d, %d, %d (%d) [%s]\n",
+			   inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip_dst,
+			   dstbuf, sizeof(dstbuf)),
+		       inet_ntop(AF_INET6, em_route_base_v6[i].v6_key.ip_src,
+			   srcbuf, sizeof(srcbuf)),
+			   em_route_base_v6[i].v6_key.port_dst,
+			   em_route_base_v6[i].v6_key.port_src,
+			   em_route_base_v6[i].v6_key.proto,
+		       em_route_base_v6[i].if_out, dev_info.device->name);
 	}
 	printf("Hash: Adding 0x%" PRIx64 "keys\n",
-		(uint64_t)IPV6_L3FWD_EM_NUM_ROUTES);
-}
-
-#define NUMBER_PORT_USED 16
-static inline void
-populate_ipv4_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask0 = (rte_xmm_t){.u32 = {BIT_8_TO_15, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv4_l3fwd_em_route entry;
-		union ipv4_5tuple_host newkey;
-
-		uint8_t a = (uint8_t)((port + 1) % BYTE_VALUE_MAX);
-
-		/* Create the ipv4 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv4_l3fwd_em_route_array[port];
-		entry.key.ip_dst = RTE_IPV4(198, 18, port, a);
-		convert_ipv4_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv4_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
-}
-
-static inline void
-populate_ipv6_many_flow_into_table(const struct rte_hash *h,
-		unsigned int nr_flow)
-{
-	unsigned i;
-
-	mask1 = (rte_xmm_t){.u32 = {BIT_16_TO_23, ALL_32_BITS,
-				ALL_32_BITS, ALL_32_BITS} };
-	mask2 = (rte_xmm_t){.u32 = {ALL_32_BITS, ALL_32_BITS, 0, 0} };
-
-	for (i = 0; i < nr_flow; i++) {
-		uint8_t port = i % NUMBER_PORT_USED;
-		struct ipv6_l3fwd_em_route entry;
-		union ipv6_5tuple_host newkey;
-
-		/* Create the ipv6 exact match flow */
-		memset(&entry, 0, sizeof(entry));
-		entry = ipv6_l3fwd_em_route_array[port];
-		entry.key.ip_dst[15] = (port + 1) % BYTE_VALUE_MAX;
-		convert_ipv6_5tuple(&entry.key, &newkey);
-		int32_t ret = rte_hash_add_key(h, (void *) &newkey);
-
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "Unable to add entry %u\n", i);
-
-		ipv6_l3fwd_out_if[ret] = (uint8_t) entry.if_out;
-
-	}
-	printf("Hash: Adding 0x%x keys\n", nr_flow);
+		(uint64_t)route_num_v6);
 }
 
 /* Requirements:
@@ -1017,35 +969,18 @@ setup_hash(const int socketid)
 			"Unable to create the l3fwd hash on socket %d\n",
 			socketid);
 
-	if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
-		/* For testing hash matching with a large number of flows we
-		 * generate millions of IP 5-tuples with an incremented dst
-		 * address to initialize the hash table. */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_many_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_many_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid],
-				hash_entry_number);
-		}
+	/*
+	 * Use data from ipv4/ipv6 l3fwd config file
+	 * directly to initialize the hash table.
+	 */
+	if (ipv6 == 0) {
+		/* populate the ipv4 hash */
+		populate_ipv4_flow_into_table(
+			ipv4_l3fwd_em_lookup_struct[socketid]);
 	} else {
-		/*
-		 * Use data in ipv4/ipv6 l3fwd lookup table
-		 * directly to initialize the hash table.
-		 */
-		if (ipv6 == 0) {
-			/* populate the ipv4 hash */
-			populate_ipv4_few_flow_into_table(
-				ipv4_l3fwd_em_lookup_struct[socketid]);
-		} else {
-			/* populate the ipv6 hash */
-			populate_ipv6_few_flow_into_table(
-				ipv6_l3fwd_em_lookup_struct[socketid]);
-		}
+		/* populate the ipv6 hash */
+		populate_ipv6_flow_into_table(
+			ipv6_l3fwd_em_lookup_struct[socketid]);
 	}
 }
 /* >8 End of initialization of hash parameters. */
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index 63c0b4dde5..c244837745 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -33,6 +33,22 @@ struct ipv6_l3fwd_route {
 	uint8_t if_out;
 };
 
+struct ipv4_5tuple {
+	uint32_t ip_dst;
+	uint32_t ip_src;
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
+
+struct ipv6_5tuple {
+	uint8_t  ip_dst[IPV6_ADDR_LEN];
+	uint8_t  ip_src[IPV6_ADDR_LEN];
+	uint16_t port_dst;
+	uint16_t port_src;
+	uint8_t  proto;
+} __rte_packed;
+
 struct lpm_route_rule {
 	union {
 		uint32_t ip;
@@ -45,15 +61,35 @@ struct lpm_route_rule {
 	uint8_t if_out;
 };
 
+struct ipv4_l3fwd_em_route {
+	struct ipv4_5tuple key;
+	uint8_t if_out;
+};
+
+struct ipv6_l3fwd_em_route {
+	struct ipv6_5tuple key;
+	uint8_t if_out;
+};
+
+struct em_rule {
+		union {
+		struct ipv4_5tuple v4_key;
+		struct ipv6_5tuple v6_key;
+	};
+	uint8_t if_out;
+};
+
 extern struct lpm_route_rule *route_base_v4;
 extern struct lpm_route_rule *route_base_v6;
 extern int route_num_v4;
 extern int route_num_v6;
 
 extern const struct ipv4_l3fwd_route ipv4_l3fwd_route_array[16];
-
 extern const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[16];
 
+extern const struct ipv4_l3fwd_em_route ipv4_l3fwd_em_route_array[16];
+extern const struct ipv6_l3fwd_em_route ipv6_l3fwd_em_route_array[16];
+
 void
 read_config_files_lpm(void);
 
-- 
2.25.1


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

* Re: [PATCH v6 0/2] Add config file support for l3fwd
  2022-03-01 14:49         ` [PATCH v6 " Sean Morrissey
  2022-03-01 14:49           ` [PATCH v6 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
  2022-03-01 14:49           ` [PATCH v6 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
@ 2022-03-08  8:57           ` Thomas Monjalon
  2 siblings, 0 replies; 46+ messages in thread
From: Thomas Monjalon @ 2022-03-08  8:57 UTC (permalink / raw)
  To: Sean Morrissey; +Cc: dev, konstantin.ananyev

01/03/2022 15:49, Sean Morrissey:
> This patchset introduces config file support for l3fwd
> and its lookup methods LPM, FIB, and EM, similar to
> that of l3fwd-acl. This allows for route rules to be
> defined in configuration files and edited there instead
> of in each of the lookup methods hardcoded route tables.
> 
> V6:
> * Move config file parsing functions to separate files
>   as to not bloat l3fwd code.

Applied with parsing inside separate files of l3fwd.




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

end of thread, other threads:[~2022-03-08  8:57 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-16 15:01 [PATCH v1 0/2] Add config file support for l3fwd Sean Morrissey
2021-12-16 15:01 ` [PATCH v1 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
2021-12-16 15:01 ` [PATCH v1 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
2021-12-20 11:08 ` [PATCH v2 0/2] Add config file support for l3fwd Sean Morrissey
2021-12-20 11:08   ` [PATCH v2 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
2021-12-20 15:42     ` Ananyev, Konstantin
2021-12-20 11:08   ` [PATCH v2 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
2021-12-20 15:53     ` Ananyev, Konstantin
2021-12-21 12:30   ` [PATCH v3 0/2] Add config file support for l3fwd Sean Morrissey
2021-12-21 12:30     ` [PATCH v3 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
2021-12-21 12:30     ` [PATCH v3 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
2022-01-10 14:00       ` Ananyev, Konstantin
2022-01-26 12:44     ` [PATCH v4 0/2] Add config file support for l3fwd Sean Morrissey
2022-01-26 12:44       ` [PATCH v4 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
2022-01-26 12:44       ` [PATCH v4 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
2022-02-04 19:59       ` [PATCH v5 0/2] Add config file support for l3fwd Sean Morrissey
2022-02-04 19:59         ` [PATCH v5 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
2022-02-08  2:21           ` Han, YingyaX
2022-02-04 19:59         ` [PATCH v5 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
2022-02-04 22:26         ` [PATCH v5 0/2] Add config file support for l3fwd Stephen Hemminger
2022-02-06 15:16           ` Ananyev, Konstantin
2022-02-08  3:04             ` Stephen Hemminger
2022-02-08 10:44               ` Ananyev, Konstantin
2022-02-08 16:15                 ` Medvedkin, Vladimir
2022-02-08 17:49                   ` Stephen Hemminger
2022-02-08 18:10                     ` Bruce Richardson
2022-02-09 12:00                     ` Ananyev, Konstantin
2022-02-09 13:54                       ` Bruce Richardson
2022-02-09 16:00                         ` Medvedkin, Vladimir
2022-02-22  9:59                       ` Thomas Monjalon
2022-02-22 10:39                         ` Ananyev, Konstantin
2022-02-22 13:46                           ` Thomas Monjalon
2022-02-22 15:13                             ` Ananyev, Konstantin
2022-02-22 16:48                               ` Thomas Monjalon
2022-02-24 11:06                                 ` Ananyev, Konstantin
2022-02-24 13:46                                   ` Thomas Monjalon
2022-02-24 13:58                                     ` Bruce Richardson
2022-02-25 10:36                                       ` Ananyev, Konstantin
2022-02-25 10:40                                         ` Bruce Richardson
2022-02-25 12:21                                           ` Ananyev, Konstantin
2022-02-25 12:50                                           ` Morten Brørup
2022-02-25  5:18                                     ` Honnappa Nagarahalli
2022-03-01 14:49         ` [PATCH v6 " Sean Morrissey
2022-03-01 14:49           ` [PATCH v6 1/2] examples/l3fwd: add config file support for LPM/FIB Sean Morrissey
2022-03-01 14:49           ` [PATCH v6 2/2] examples/l3fwd: add config file support for EM Sean Morrissey
2022-03-08  8:57           ` [PATCH v6 0/2] Add config file support for l3fwd 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).