[v3,2/2] examples/l3fwd: add config file support for EM

Message ID 20211221123037.1037203-3-sean.morrissey@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series Add config file support for l3fwd |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-mellanox-Performance success Performance Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-x86_64-unit-testing success Testing PASS
ci/iol-x86_64-compile-testing success Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-aarch64-unit-testing success Testing PASS
ci/iol-aarch64-compile-testing success Testing PASS
ci/github-robot: build success github build: passed
ci/Intel-compilation success Compilation OK
ci/intel-Testing success Testing PASS

Commit Message

Sean Morrissey Dec. 21, 2021, 12:30 p.m. UTC
  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
  

Comments

Ananyev, Konstantin Jan. 10, 2022, 2 p.m. UTC | #1
> 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
  

Patch

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;