diff mbox series

[v5] examples/l3fwd: merge l3fwd-acl into l3fwd

Message ID 20220411084127.3735312-1-sean.morrissey@intel.com (mailing list archive)
State Superseded
Delegated to: Thomas Monjalon
Headers show
Series [v5] examples/l3fwd: merge l3fwd-acl into l3fwd | expand

Checks

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

Commit Message

Morrissey, Sean April 11, 2022, 8:41 a.m. UTC
l3fwd-acl contains duplicate functions to l3fwd.
For this reason we merge l3fwd-acl code into l3fwd
with '--lookup acl' cmdline option to run ACL.

Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
---
V5:
* remove undefined functions
* remove unused struct members
V4:
* update maintainers
* fix doc changes
V3:
* remove unnecessary declarations
* move functions to correct files
V2:
* add doc changes
* minor code cleanup
---
 MAINTAINERS                                   |    2 -
 doc/guides/rel_notes/release_22_07.rst        |    5 +
 doc/guides/sample_app_ug/index.rst            |    1 -
 doc/guides/sample_app_ug/l3_forward.rst       |   63 +-
 .../sample_app_ug/l3_forward_access_ctrl.rst  |  340 ---
 examples/l3fwd-acl/Makefile                   |   51 -
 examples/l3fwd-acl/main.c                     | 2272 -----------------
 examples/l3fwd-acl/meson.build                |   13 -
 examples/l3fwd/Makefile                       |    2 +-
 examples/l3fwd/l3fwd.h                        |   31 +-
 examples/l3fwd/l3fwd_acl.c                    | 1098 ++++++++
 examples/l3fwd/l3fwd_acl.h                    |   51 +
 examples/l3fwd/l3fwd_acl_scalar.h             |  112 +
 examples/l3fwd/l3fwd_route.h                  |   16 +
 examples/l3fwd/main.c                         |   65 +-
 examples/l3fwd/meson.build                    |    3 +-
 examples/meson.build                          |    1 -
 17 files changed, 1426 insertions(+), 2700 deletions(-)
 delete mode 100644 doc/guides/sample_app_ug/l3_forward_access_ctrl.rst
 delete mode 100644 examples/l3fwd-acl/Makefile
 delete mode 100644 examples/l3fwd-acl/main.c
 delete mode 100644 examples/l3fwd-acl/meson.build
 create mode 100644 examples/l3fwd/l3fwd_acl.c
 create mode 100644 examples/l3fwd/l3fwd_acl.h
 create mode 100644 examples/l3fwd/l3fwd_acl_scalar.h

Comments

Ananyev, Konstantin April 11, 2022, 11:38 a.m. UTC | #1
> 
> l3fwd-acl contains duplicate functions to l3fwd.
> For this reason we merge l3fwd-acl code into l3fwd
> with '--lookup acl' cmdline option to run ACL.
> 
> Signed-off-by: Sean Morrissey <sean.morrissey@intel.com>
> ---
> V5:
> * remove undefined functions
> * remove unused struct members
> V4:
> * update maintainers
> * fix doc changes
> V3:
> * remove unnecessary declarations
> * move functions to correct files
> V2:
> * add doc changes
> * minor code cleanup
> ---
>  MAINTAINERS                                   |    2 -
>  doc/guides/rel_notes/release_22_07.rst        |    5 +
>  doc/guides/sample_app_ug/index.rst            |    1 -
>  doc/guides/sample_app_ug/l3_forward.rst       |   63 +-
>  .../sample_app_ug/l3_forward_access_ctrl.rst  |  340 ---
>  examples/l3fwd-acl/Makefile                   |   51 -
>  examples/l3fwd-acl/main.c                     | 2272 -----------------
>  examples/l3fwd-acl/meson.build                |   13 -
>  examples/l3fwd/Makefile                       |    2 +-
>  examples/l3fwd/l3fwd.h                        |   31 +-
>  examples/l3fwd/l3fwd_acl.c                    | 1098 ++++++++
>  examples/l3fwd/l3fwd_acl.h                    |   51 +
>  examples/l3fwd/l3fwd_acl_scalar.h             |  112 +
>  examples/l3fwd/l3fwd_route.h                  |   16 +
>  examples/l3fwd/main.c                         |   65 +-
>  examples/l3fwd/meson.build                    |    3 +-
>  examples/meson.build                          |    1 -
>  17 files changed, 1426 insertions(+), 2700 deletions(-)
>  delete mode 100644 doc/guides/sample_app_ug/l3_forward_access_ctrl.rst
>  delete mode 100644 examples/l3fwd-acl/Makefile
>  delete mode 100644 examples/l3fwd-acl/main.c
>  delete mode 100644 examples/l3fwd-acl/meson.build
>  create mode 100644 examples/l3fwd/l3fwd_acl.c
>  create mode 100644 examples/l3fwd/l3fwd_acl.h
>  create mode 100644 examples/l3fwd/l3fwd_acl_scalar.h
> 

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

> --
> 2.25.1
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 15008c03bc..b29ff8929d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1505,8 +1505,6 @@  F: lib/acl/
 F: doc/guides/prog_guide/packet_classif_access_ctrl.rst
 F: app/test-acl/
 F: app/test/test_acl.*
-F: examples/l3fwd-acl/
-F: doc/guides/sample_app_ug/l3_forward_access_ctrl.rst
 
 EFD
 M: Byron Marohn <byron.marohn@intel.com>
diff --git a/doc/guides/rel_notes/release_22_07.rst b/doc/guides/rel_notes/release_22_07.rst
index 42a5f2d990..204dd028da 100644
--- a/doc/guides/rel_notes/release_22_07.rst
+++ b/doc/guides/rel_notes/release_22_07.rst
@@ -55,6 +55,11 @@  New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Merged l3fwd-acl into l3fwd.**
+
+  Merged l3fwd-acl code into l3fwd as l3fwd-acl contains duplicate and
+  common functions to l3fwd.
+
 
 Removed Items
 -------------
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index 853e338778..cc9fae1e8c 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -31,7 +31,6 @@  Sample Applications User Guides
     l3_forward
     l3_forward_graph
     l3_forward_power_man
-    l3_forward_access_ctrl
     link_status_intr
     server_node_efd
     service_cores
diff --git a/doc/guides/sample_app_ug/l3_forward.rst b/doc/guides/sample_app_ug/l3_forward.rst
index 01d86db95d..e82168710d 100644
--- a/doc/guides/sample_app_ug/l3_forward.rst
+++ b/doc/guides/sample_app_ug/l3_forward.rst
@@ -11,7 +11,7 @@  The application performs L3 forwarding.
 Overview
 --------
 
-The application demonstrates the use of the hash, LPM and FIB libraries in DPDK
+The application demonstrates the use of the hash, LPM, FIB and ACL libraries in DPDK
 to implement packet forwarding using poll or event mode PMDs for packet I/O.
 The initialization and run-time paths are very similar to those of the
 :doc:`l2_forward_real_virtual` and :doc:`l2_forward_event`.
@@ -22,7 +22,7 @@  decision is made based on information read from the input packet.
 Eventdev can optionally use S/W or H/W (if supported by platform) scheduler
 implementation for packet I/O based on run time parameters.
 
-The lookup method is hash-based, LPM-based or FIB-based
+The lookup method is hash-based, LPM-based, FIB-based or ACL-based
 and is selected at run time.
 When the selected lookup method is hash-based,
 a hash object is used to emulate the flow classification stage.
@@ -44,7 +44,15 @@  returned by the LPM or FIB lookup.
 The set of LPM and FIB rules used by the application is statically configured
 and loaded into the LPM or FIB object at initialization time.
 
-In the sample application, hash-based and FIB-based forwarding supports
+For ACL the ACL library is used to perform both ACL and route entry lookup.
+When packets are received from a port,
+the application extracts the necessary information from the TCP/IP header of the received packet and
+performs a lookup in the rule database to figure out whether the packets should be dropped (in the ACL range)
+or forwarded to desired ports.
+For ACL the application implements packet classification for the IPv4/IPv6 5-tuple syntax specifically.
+The 5-tuple syntax consist of a source IP address, a destination IP address, a source port, a destination port and a protocol identifier.
+
+In the sample application, hash-based, FIB-based and ACL-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.
@@ -97,7 +105,8 @@  Where,
   Accepted options:
   ``em`` (Exact Match),
   ``lpm`` (Longest Prefix Match),
-  ``fib`` (Forwarding Information Base).
+  ``fib`` (Forwarding Information Base),
+  ``acl`` (Access Control List).
   Default is ``lpm``.
 
 * ``--config (port,queue,lcore)[,(port,queue,lcore)]:`` Determines which queues from which ports are mapped to which cores.
@@ -128,6 +137,9 @@  Where,
 
 * ``--event-vector-tmo:`` Optional, Max timeout to form vector in nanoseconds if event vectorization is enabled.
 
+* ``--alg=<val>:`` optional, ACL classify method to use, one of:
+  ``scalar|sse|avx2|neon|altivec|avx512x16|avx512x32``
+
 * ``-E:`` Optional, enable exact match,
   legacy flag, please use ``--lookup=em`` instead.
 
@@ -248,6 +260,8 @@  To read data from the specified file successfully, the application assumes the f
 
 *   Comment line, which starts with a leading character '#'
 
+*   ACL rule 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').
 
@@ -264,13 +278,27 @@  R<destination_ip>/<ip_mask_length><output_port_number>
 
 R<destination_ip><source_ip><destination_port><source_port><protocol><output_port_number>
 
+*   A typical IPv4 ACL rule line should have a format as shown below:
+
+
+.. _figure_ipv4_acl_rule:
+
+.. figure:: img/ipv4_acl_rule.*
+
+   A typical IPv4 ACL rule
+
+
 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 LPM/FIB/ACL 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.
 
+The application parses the rules from the file, 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 ACL rules save the index to the specific rules in the userdata field, while route rules save the forwarding port number.
+
 Hash Initialization
 ~~~~~~~~~~~~~~~~~~~
 
@@ -334,6 +362,31 @@  the full setup function including the IPv6 setup can be seen in the app code.
    :start-after: Function to setup fib. 8<
    :end-before: >8 End of setup fib.
 
+ACL Initialization
+~~~~~~~~~~~~~~~~~~
+
+For each supported AC rule format (IPv4 5-tuple, IPv6 6-tuple) application creates a separate context handler
+from the ACL library for each CPU socket on the board and adds parsed rules into that context.
+
+Note, that for each supported rule type,
+application needs to calculate the expected offset of the fields from the start of the packet.
+That's why only packets with fixed IPv4/ IPv6 header are supported.
+That allows to perform ACL classify straight over incoming packet buffer -
+no extra protocol field retrieval need to be performed.
+
+Subsequently, the application checks whether NUMA is enabled.
+If it is, the application records the socket IDs of the CPU cores involved in the task.
+
+Finally, the application creates contexts handler from the ACL library,
+adds rules parsed from the file into the database and build an ACL trie.
+It is important to note that the application creates an independent copy of each database for each socket CPU
+involved in the task to reduce the time for remote memory access.
+
+.. literalinclude:: ../../../examples/l3fwd/l3fwd_acl.c
+   :language: c
+   :start-after: Setup ACL context. 8<
+   :end-before: >8 End of ACL context setup.
+
 Packet Forwarding for Hash-based Lookups
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/doc/guides/sample_app_ug/l3_forward_access_ctrl.rst b/doc/guides/sample_app_ug/l3_forward_access_ctrl.rst
deleted file mode 100644
index ecb1c857c4..0000000000
--- a/doc/guides/sample_app_ug/l3_forward_access_ctrl.rst
+++ /dev/null
@@ -1,340 +0,0 @@ 
-..  SPDX-License-Identifier: BSD-3-Clause
-    Copyright(c) 2010-2014 Intel Corporation.
-
-L3 Forwarding with Access Control Sample Application
-====================================================
-
-The L3 Forwarding with Access Control application is a simple example of packet processing using the DPDK.
-The application performs a security check on received packets.
-Packets that are in the Access Control List (ACL), which is loaded during initialization, are dropped.
-Others are forwarded to the correct port.
-
-Overview
---------
-
-The application demonstrates the use of the ACL library in the DPDK to implement access control
-and packet L3 forwarding.
-The application loads two types of rules at initialization:
-
-*   Route information rules, which are used for L3 forwarding
-
-*   Access Control List (ACL) rules that block packets with a specific characteristic
-
-When packets are received from a port,
-the application extracts the necessary information from the TCP/IP header of the received packet and
-performs a lookup in the rule database to figure out whether the packets should be dropped (in the ACL range)
-or forwarded to desired ports.
-The initialization and run-time paths are similar to those of the :doc:`l3_forward`.
-However, there are significant differences in the two applications.
-For example, the original L3 forwarding application uses either LPM or
-an exact match algorithm to perform forwarding port lookup,
-while this application uses the ACL library to perform both ACL and route entry lookup.
-The following sections provide more detail.
-
-Classification for both IPv4 and IPv6 packets is supported in this application.
-The application also assumes that all the packets it processes are TCP/UDP packets and
-always extracts source/destination port information from the packets.
-
-Tuple Packet Syntax
-~~~~~~~~~~~~~~~~~~~
-
-The application implements packet classification for the IPv4/IPv6 5-tuple syntax specifically.
-The 5-tuple syntax consist of a source IP address, a destination IP address,
-a source port, a destination port and a protocol identifier.
-The fields in the 5-tuple syntax have the following formats:
-
-*   **Source IP address and destination IP address**
-    : Each is either a 32-bit field (for IPv4), or a set of 4 32-bit fields (for IPv6) represented by a value and a mask length.
-    For example, an IPv4 range of 192.168.1.0 to 192.168.1.255 could be represented by a value = [192, 168, 1, 0] and a mask length = 24.
-
-*   **Source port and destination port**
-    : Each is a 16-bit field, represented by a lower start and a higher end.
-    For example, a range of ports 0 to 8192 could be represented by lower = 0 and higher = 8192.
-
-*   **Protocol identifier**
-    : An 8-bit field, represented by a value and a mask, that covers a range of values.
-    To verify that a value is in the range, use the following expression: "(VAL & mask) == value"
-
-The trick in how to represent a range with a mask and value is as follows.
-A range can be enumerated in binary numbers with some bits that are never changed and some bits that are dynamically changed.
-Set those bits that dynamically changed in mask and value with 0.
-Set those bits that never changed in the mask with 1, in value with number expected.
-For example, a range of 6 to 7 is enumerated as 0b110 and 0b111.
-Bit 1-7 are bits never changed and bit 0 is the bit dynamically changed.
-Therefore, set bit 0 in mask and value with 0, set bits 1-7 in mask with 1, and bits 1-7 in value with number 0b11.
-So, mask is 0xfe, value is 0x6.
-
-.. note::
-
-    The library assumes that each field in the rule is in LSB or Little Endian order when creating the database.
-    It internally converts them to MSB or Big Endian order.
-    When performing a lookup, the library assumes the input is in MSB or Big Endian order.
-
-Access Rule Syntax
-~~~~~~~~~~~~~~~~~~
-
-In this sample application, each rule is a combination of the following:
-
-*   5-tuple field: This field has a format described in Section.
-
-*   priority field: A weight to measure the priority of the rules.
-    The rule with the higher priority will ALWAYS be returned if the specific input has multiple matches in the rule database.
-    Rules with lower priority will NEVER be returned in any cases.
-
-*   userdata field: A user-defined field that could be any value.
-    It can be the forwarding port number if the rule is a route table entry or it can be a pointer to a mapping address
-    if the rule is used for address mapping in the NAT application.
-    The key point is that it is a useful reserved field for user convenience.
-
-ACL and Route Rules
-~~~~~~~~~~~~~~~~~~~
-
-The application needs to acquire ACL and route rules before it runs.
-Route rules are mandatory, while ACL rules are optional.
-To simplify the complexity of the priority field for each rule, all ACL and route entries are assumed to be in the same file.
-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:
-
-*   ACL rule line, which starts with a leading character '@'
-
-*   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 ACL rule line should have a format as shown below:
-
-
-.. _figure_ipv4_acl_rule:
-
-.. figure:: img/ipv4_acl_rule.*
-
-   A typical IPv4 ACL rule
-
-
-IPv4 addresses are specified in CIDR format as specified in RFC 4632.
-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.
-
-Ports are specified as a range of 16-bit numbers in the format MIN:MAX,
-where MIN and MAX are the inclusive minimum and maximum values of the range.
-The range 0:65535 represents all possible ports in a range.
-When MIN and MAX are the same value, a single port is represented, for example, 20:20.
-
-The protocol identifier is an 8-bit value and a mask separated by '/'.
-For example: 6/0xfe matches protocol values 6 and 7.
-
-*   Route rules start with a leading character 'R' and have the same format as ACL rules except an extra field at the tail
-    that indicates the forwarding port number.
-
-Rules File Example
-~~~~~~~~~~~~~~~~~~
-
-.. _figure_example_rules:
-
-.. figure:: img/example_rules.*
-
-   Rules example
-
-
-Each rule is explained as follows:
-
-*   Rule 1 (the first line) tells the application to drop those packets with source IP address = [1.2.3.*],
-    destination IP address = [192.168.0.36], protocol = [6]/[7]
-
-*   Rule 2 (the second line) is similar to Rule 1, except the source IP address is ignored.
-    It tells the application to forward packets with destination IP address = [192.168.0.36],
-    protocol = [6]/[7], destined to port 1.
-
-*   Rule 3 (the third line) tells the application to forward all packets to port 0.
-    This is something like a default route entry.
-
-As described earlier, the application assume rules are listed in descending order of priority,
-therefore Rule 1 has the highest priority, then Rule 2, and finally,
-Rule 3 has the lowest priority.
-
-Consider the arrival of the following three packets:
-
-*   Packet 1 has source IP address = [1.2.3.4], destination IP address = [192.168.0.36], and protocol = [6]
-
-*   Packet 2 has source IP address = [1.2.4.4], destination IP address = [192.168.0.36], and protocol = [6]
-
-*   Packet 3 has source IP address = [1.2.3.4], destination IP address = [192.168.0.36], and protocol = [8]
-
-Observe that:
-
-*   Packet 1 matches all of the rules
-
-*   Packet 2 matches Rule 2 and Rule 3
-
-*   Packet 3 only matches Rule 3
-
-For priority reasons, Packet 1 matches Rule 1 and is dropped.
-Packet 2 matches Rule 2 and is forwarded to port 1.
-Packet 3 matches Rule 3 and is forwarded to port 0.
-
-For more details on the rule file format,
-please refer to rule_ipv4.db and rule_ipv6.db files (inside dpdk/examples/l3fwd-acl/).
-
-Application Phases
-~~~~~~~~~~~~~~~~~~
-
-Once the application starts, it transitions through three phases:
-
-*   **Initialization Phase**
-    - Perform the following tasks:
-
-*   Parse command parameters. Check the validity of rule file(s) name(s), number of logical cores, receive and transmit queues.
-    Bind ports, queues and logical cores. Check ACL search options, and so on.
-
-*   Call Environmental Abstraction Layer (EAL) and Poll Mode Driver (PMD) functions to initialize the environment and detect possible NICs.
-    The EAL creates several threads and sets affinity to a specific hardware thread CPU based on the configuration specified
-    by the command line arguments.
-
-*   Read the rule files and format the rules into the representation that the ACL library can recognize.
-    Call the ACL library function to add the rules into the database and compile them as a trie of pattern sets.
-    Note that application maintains a separate AC contexts for IPv4 and IPv6 rules.
-
-*   **Runtime Phase**
-    - Process the incoming packets from a port. Packets are processed in three steps:
-
-    *   Retrieval: Gets a packet from the receive queue. Each logical core may process several queues for different ports.
-        This depends on the configuration specified by command line arguments.
-
-    *   Lookup: Checks that the packet type is supported (IPv4/IPv6) and performs a 5-tuple lookup over corresponding AC context.
-        If an ACL rule is matched, the packets will be dropped and return back to step 1.
-        If a route rule is matched, it indicates the packet is not in the ACL list and should be forwarded.
-        If there is no matches for the packet, then the packet is dropped.
-
-    *   Forwarding: Forwards the packet to the corresponding port.
-
-*   **Final Phase** - Perform the following tasks:
-
-    Calls the EAL, PMD and ACL library to free resource, then quits.
-
-Compiling the Application
--------------------------
-
-To compile the sample application see :doc:`compiling`.
-
-The application is located in the ``l3fwd-acl`` sub-directory.
-
-Running the Application
------------------------
-
-The application has a number of command line options:
-
-..  code-block:: console
-
-    ./<build_dir>/examples/dpdk-l3fwd-acl [EAL options] -- -p PORTMASK [-P] --config(port,queue,lcore)[,(port,queue,lcore)] --rule_ipv4 FILENAME --rule_ipv6 FILENAME [--alg=<val>] [--max-pkt-len PKTLEN] [--no-numa] [--eth-dest=X,MM:MM:MM:MM:MM:MM]
-
-
-where,
-
-*   -p PORTMASK: Hexadecimal bitmask of ports to configure
-
-*   -P: 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.
-
-*   --config (port,queue,lcore)[,(port,queue,lcore)]: determines which queues from which ports are mapped to which cores
-
-*   --rule_ipv4 FILENAME: Specifies the IPv4 ACL and route rules file
-
-*   --rule_ipv6 FILENAME: Specifies the IPv6 ACL and route rules file
-
-*   --alg=<val>: optional, ACL classify method to use, one of:
-    ``scalar|sse|avx2|neon|altivec|avx512x16|avx512x32``
-
-*   --max-pkt-len: optional, maximum packet length in decimal (64-9600)
-
-*   --no-numa: optional, disables numa awareness
-
-*   --eth-dest=X,MM:MM:MM:MM:MM:MM: optional, ethernet destination for port X
-
-For example, consider a dual processor socket platform with 8 physical cores, where cores 0-7 and 16-23 appear on socket 0,
-while cores 8-15 and 24-31 appear on socket 1.
-
-To enable L3 forwarding between two ports, assuming that both ports are in the same socket, using two cores, cores 1 and 2,
-(which are in the same socket too), use the following command:
-
-..  code-block:: console
-
-    ./<build_dir>/examples/dpdk-l3fwd-acl -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)" --rule_ipv4="rule_ipv4.db" --rule_ipv6="rule_ipv6.db" --alg=scalar
-
-In this command:
-
-*   The -l option enables cores 1, 2
-
-*   The -p option enables ports 0 and 1
-
-*   The --config option enables one queue on each port and maps each (port,queue) pair to a specific core.
-    The following table shows the mapping in this example:
-
-    +----------+------------+-----------+-------------------------------------+
-    | **Port** | **Queue**  | **lcore** |            **Description**          |
-    |          |            |           |                                     |
-    +==========+============+===========+=====================================+
-    | 0        | 0          | 1         | Map queue 0 from port 0 to lcore 1. |
-    |          |            |           |                                     |
-    +----------+------------+-----------+-------------------------------------+
-    | 1        | 0          | 2         | Map queue 0 from port 1 to lcore 2. |
-    |          |            |           |                                     |
-    +----------+------------+-----------+-------------------------------------+
-
-*   The --rule_ipv4 option specifies the reading of IPv4 rules sets from the rule_ipv4.db file.
-
-*   The --rule_ipv6 option specifies the reading of IPv6 rules sets from the rule_ipv6.db file.
-
-*   The --alg=scalar option specifies the performing of rule lookup with a scalar function.
-
-Explanation
------------
-
-The following sections provide some explanation of the sample application code.
-The aspects of port, device and CPU configuration are similar to those of the :doc:`l3_forward`.
-The following sections describe aspects that are specific to L3 forwarding with access control.
-
-Parse Rules from File
-~~~~~~~~~~~~~~~~~~~~~
-
-As described earlier, both ACL and route rules are assumed to be saved in the same file.
-The application parses the rules from the file and adds them to the database by calling the ACL library 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 application needs to consider the userdata and priority fields.
-The ACL rules save the index to the specific rules in the userdata field,
-while route rules save the forwarding port number.
-In order to differentiate the two types of rules, ACL rules add a signature in the userdata field.
-As for the priority field, the application assumes rules are organized in descending order of priority.
-Therefore, the code only decreases the priority number with each rule it parses.
-
-Setting Up the ACL Context
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-For each supported AC rule format (IPv4 5-tuple, IPv6 6-tuple) application creates a separate context handler
-from the ACL library for each CPU socket on the board and adds parsed rules into that context.
-
-Note, that for each supported rule type,
-application needs to calculate the expected offset of the fields from the start of the packet.
-That's why only packets with fixed IPv4/ IPv6 header are supported.
-That allows to perform ACL classify straight over incoming packet buffer -
-no extra protocol field retrieval need to be performed.
-
-Subsequently, the application checks whether NUMA is enabled.
-If it is, the application records the socket IDs of the CPU cores involved in the task.
-
-Finally, the application creates contexts handler from the ACL library,
-adds rules parsed from the file into the database and build an ACL trie.
-It is important to note that the application creates an independent copy of each database for each socket CPU
-involved in the task to reduce the time for remote memory access.
diff --git a/examples/l3fwd-acl/Makefile b/examples/l3fwd-acl/Makefile
deleted file mode 100644
index 85fd2c47a1..0000000000
--- a/examples/l3fwd-acl/Makefile
+++ /dev/null
@@ -1,51 +0,0 @@ 
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2010-2014 Intel Corporation
-
-# binary name
-APP = l3fwd-acl
-
-# all source are stored in SRCS-y
-SRCS-y := main.c
-
-PKGCONF ?= pkg-config
-
-# Build using pkg-config variables if possible
-ifneq ($(shell $(PKGCONF) --exists libdpdk && echo 0),0)
-$(error "no installation of DPDK found")
-endif
-
-all: shared
-.PHONY: shared static
-shared: build/$(APP)-shared
-	ln -sf $(APP)-shared build/$(APP)
-static: build/$(APP)-static
-	ln -sf $(APP)-static build/$(APP)
-
-PC_FILE := $(shell $(PKGCONF) --path libdpdk 2>/dev/null)
-CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk)
-LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
-LDFLAGS_STATIC = $(shell $(PKGCONF) --static --libs libdpdk)
-
-ifeq ($(MAKECMDGOALS),static)
-# check for broken pkg-config
-ifeq ($(shell echo $(LDFLAGS_STATIC) | grep 'whole-archive.*l:lib.*no-whole-archive'),)
-$(warning "pkg-config output list does not contain drivers between 'whole-archive'/'no-whole-archive' flags.")
-$(error "Cannot generate statically-linked binaries with this version of pkg-config")
-endif
-endif
-
-CFLAGS += -DALLOW_EXPERIMENTAL_API
-
-build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
-	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
-
-build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
-	$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)
-
-build:
-	@mkdir -p $@
-
-.PHONY: clean
-clean:
-	rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
-	test -d build && rmdir -p build || true
diff --git a/examples/l3fwd-acl/main.c b/examples/l3fwd-acl/main.c
deleted file mode 100644
index 2d2ecc7635..0000000000
--- a/examples/l3fwd-acl/main.c
+++ /dev/null
@@ -1,2272 +0,0 @@ 
-/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2010-2016 Intel Corporation
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <string.h>
-#include <sys/queue.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <getopt.h>
-
-#include <rte_common.h>
-#include <rte_byteorder.h>
-#include <rte_log.h>
-#include <rte_memory.h>
-#include <rte_memcpy.h>
-#include <rte_eal.h>
-#include <rte_launch.h>
-#include <rte_cycles.h>
-#include <rte_prefetch.h>
-#include <rte_lcore.h>
-#include <rte_per_lcore.h>
-#include <rte_branch_prediction.h>
-#include <rte_interrupts.h>
-#include <rte_random.h>
-#include <rte_debug.h>
-#include <rte_ether.h>
-#include <rte_ethdev.h>
-#include <rte_mempool.h>
-#include <rte_mbuf.h>
-#include <rte_ip.h>
-#include <rte_tcp.h>
-#include <rte_udp.h>
-#include <rte_string_fns.h>
-#include <rte_acl.h>
-
-#include <cmdline_parse.h>
-#include <cmdline_parse_etheraddr.h>
-
-#if RTE_LOG_DP_LEVEL >= RTE_LOG_DEBUG
-#define L3FWDACL_DEBUG
-#endif
-#define DO_RFC_1812_CHECKS
-
-#define RTE_LOGTYPE_L3FWD RTE_LOGTYPE_USER1
-
-#define MAX_JUMBO_PKT_LEN  9600
-
-#define MEMPOOL_CACHE_SIZE 256
-
-/*
- * This expression is used to calculate the number of mbufs needed
- * depending on user input, taking into account memory for rx and tx hardware
- * rings, cache per lcore and mtable per port per lcore.
- * RTE_MAX is used to ensure that NB_MBUF never goes below a
- * minimum value of 8192
- */
-
-#define NB_MBUF	RTE_MAX(\
-	(nb_ports * nb_rx_queue * nb_rxd +	\
-	nb_ports * nb_lcores * MAX_PKT_BURST +	\
-	nb_ports * n_tx_queue * nb_txd +	\
-	nb_lcores * MEMPOOL_CACHE_SIZE),	\
-	(unsigned)8192)
-
-#define MAX_PKT_BURST 32
-#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
-
-#define NB_SOCKETS 8
-
-/* Configure how many packets ahead to prefetch, when reading packets */
-#define PREFETCH_OFFSET	3
-
-/*
- * Configurable number of RX/TX ring descriptors
- */
-#define RTE_TEST_RX_DESC_DEFAULT 1024
-#define RTE_TEST_TX_DESC_DEFAULT 1024
-static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
-static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
-
-/* mask of enabled ports */
-static uint32_t enabled_port_mask;
-static int promiscuous_on; /**< Ports set in promiscuous mode off by default. */
-static int numa_on = 1; /**< NUMA is enabled by default. */
-
-struct lcore_rx_queue {
-	uint16_t port_id;
-	uint8_t queue_id;
-} __rte_cache_aligned;
-
-#define MAX_RX_QUEUE_PER_LCORE 16
-#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS
-#define MAX_RX_QUEUE_PER_PORT 128
-
-#define MAX_LCORE_PARAMS 1024
-struct lcore_params {
-	uint16_t port_id;
-	uint8_t queue_id;
-	uint8_t lcore_id;
-} __rte_cache_aligned;
-
-static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS];
-static struct lcore_params lcore_params_array_default[] = {
-	{0, 0, 2},
-	{0, 1, 2},
-	{0, 2, 2},
-	{1, 0, 2},
-	{1, 1, 2},
-	{1, 2, 2},
-	{2, 0, 2},
-	{3, 0, 3},
-	{3, 1, 3},
-};
-
-static struct lcore_params *lcore_params = lcore_params_array_default;
-static uint16_t nb_lcore_params = sizeof(lcore_params_array_default) /
-				sizeof(lcore_params_array_default[0]);
-
-static struct rte_eth_conf port_conf = {
-	.rxmode = {
-		.mq_mode	= RTE_ETH_MQ_RX_RSS,
-		.split_hdr_size = 0,
-		.offloads = RTE_ETH_RX_OFFLOAD_CHECKSUM,
-	},
-	.rx_adv_conf = {
-		.rss_conf = {
-			.rss_key = NULL,
-			.rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_UDP |
-				RTE_ETH_RSS_TCP | RTE_ETH_RSS_SCTP,
-		},
-	},
-	.txmode = {
-		.mq_mode = RTE_ETH_MQ_TX_NONE,
-	},
-};
-
-static uint32_t max_pkt_len;
-
-static struct rte_mempool *pktmbuf_pool[NB_SOCKETS];
-
-/* ethernet addresses of ports */
-static struct rte_ether_hdr port_l2hdr[RTE_MAX_ETHPORTS];
-
-static const struct {
-	const char *name;
-	enum rte_acl_classify_alg alg;
-} acl_alg[] = {
-	{
-		.name = "scalar",
-		.alg = RTE_ACL_CLASSIFY_SCALAR,
-	},
-	{
-		.name = "sse",
-		.alg = RTE_ACL_CLASSIFY_SSE,
-	},
-	{
-		.name = "avx2",
-		.alg = RTE_ACL_CLASSIFY_AVX2,
-	},
-	{
-		.name = "neon",
-		.alg = RTE_ACL_CLASSIFY_NEON,
-	},
-	{
-		.name = "altivec",
-		.alg = RTE_ACL_CLASSIFY_ALTIVEC,
-	},
-	{
-		.name = "avx512x16",
-		.alg = RTE_ACL_CLASSIFY_AVX512X16,
-	},
-	{
-		.name = "avx512x32",
-		.alg = RTE_ACL_CLASSIFY_AVX512X32,
-	},
-};
-
-/***********************start of ACL part******************************/
-#ifdef DO_RFC_1812_CHECKS
-static inline int
-is_valid_ipv4_pkt(struct rte_ipv4_hdr *pkt, uint32_t link_len);
-#endif
-static inline void
-send_single_packet(struct rte_mbuf *m, uint16_t port);
-
-#define MAX_ACL_RULE_NUM	100000
-#define DEFAULT_MAX_CATEGORIES	1
-#define L3FWD_ACL_IPV4_NAME	"l3fwd-acl-ipv4"
-#define L3FWD_ACL_IPV6_NAME	"l3fwd-acl-ipv6"
-#define ACL_LEAD_CHAR		('@')
-#define ROUTE_LEAD_CHAR		('R')
-#define COMMENT_LEAD_CHAR	('#')
-
-enum {
-#define OPT_CONFIG      "config"
-	OPT_CONFIG_NUM = 256,
-#define OPT_NONUMA      "no-numa"
-	OPT_NONUMA_NUM,
-#define OPT_MAX_PKT_LEN "max-pkt-len"
-	OPT_MAX_PKT_LEN_NUM,
-#define OPT_RULE_IPV4   "rule_ipv4"
-	OPT_RULE_IPV4_NUM,
-#define OPT_RULE_IPV6	"rule_ipv6"
-	OPT_RULE_IPV6_NUM,
-#define OPT_ALG         "alg"
-	OPT_ALG_NUM,
-#define OPT_ETH_DEST    "eth-dest"
-	OPT_ETH_DEST_NUM,
-};
-
-#define ACL_DENY_SIGNATURE	0xf0000000
-#define RTE_LOGTYPE_L3FWDACL	RTE_LOGTYPE_USER3
-#define acl_log(format, ...)	RTE_LOG(ERR, L3FWDACL, format, ##__VA_ARGS__)
-#define uint32_t_to_char(ip, a, b, c, d) do {\
-		*a = (unsigned char)(ip >> 24 & 0xff);\
-		*b = (unsigned char)(ip >> 16 & 0xff);\
-		*c = (unsigned char)(ip >> 8 & 0xff);\
-		*d = (unsigned char)(ip & 0xff);\
-	} while (0)
-#define OFF_ETHHEAD	(sizeof(struct rte_ether_hdr))
-#define OFF_IPV42PROTO (offsetof(struct rte_ipv4_hdr, next_proto_id))
-#define OFF_IPV62PROTO (offsetof(struct rte_ipv6_hdr, proto))
-#define MBUF_IPV4_2PROTO(m)	\
-	rte_pktmbuf_mtod_offset((m), uint8_t *, OFF_ETHHEAD + OFF_IPV42PROTO)
-#define MBUF_IPV6_2PROTO(m)	\
-	rte_pktmbuf_mtod_offset((m), uint8_t *, OFF_ETHHEAD + OFF_IPV62PROTO)
-
-#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)
-
-/*
-  * ACL rules should have higher priorities than route ones to ensure ACL rule
-  * always be found when input packets have multi-matches in the database.
-  * A exception case is performance measure, which can define route rules with
-  * higher priority and route rules will always be returned in each lookup.
-  * Reserve range from ACL_RULE_PRIORITY_MAX + 1 to
-  * RTE_ACL_MAX_PRIORITY for route entries in performance measure
-  */
-#define ACL_RULE_PRIORITY_MAX 0x10000000
-
-/*
-  * Forward port info save in ACL lib starts from 1
-  * since ACL assume 0 is invalid.
-  * So, need add 1 when saving and minus 1 when forwarding packets.
-  */
-#define FWD_PORT_SHIFT 1
-
-/*
- * Rule and trace formats definitions.
- */
-
-enum {
-	PROTO_FIELD_IPV4,
-	SRC_FIELD_IPV4,
-	DST_FIELD_IPV4,
-	SRCP_FIELD_IPV4,
-	DSTP_FIELD_IPV4,
-	NUM_FIELDS_IPV4
-};
-
-/*
- * That effectively defines order of IPV4VLAN classifications:
- *  - PROTO
- *  - VLAN (TAG and DOMAIN)
- *  - SRC IP ADDRESS
- *  - DST IP ADDRESS
- *  - PORTS (SRC and DST)
- */
-enum {
-	RTE_ACL_IPV4VLAN_PROTO,
-	RTE_ACL_IPV4VLAN_VLAN,
-	RTE_ACL_IPV4VLAN_SRC,
-	RTE_ACL_IPV4VLAN_DST,
-	RTE_ACL_IPV4VLAN_PORTS,
-	RTE_ACL_IPV4VLAN_NUM
-};
-
-struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
-	{
-		.type = RTE_ACL_FIELD_TYPE_BITMASK,
-		.size = sizeof(uint8_t),
-		.field_index = PROTO_FIELD_IPV4,
-		.input_index = RTE_ACL_IPV4VLAN_PROTO,
-		.offset = 0,
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = SRC_FIELD_IPV4,
-		.input_index = RTE_ACL_IPV4VLAN_SRC,
-		.offset = offsetof(struct rte_ipv4_hdr, src_addr) -
-			offsetof(struct rte_ipv4_hdr, next_proto_id),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = DST_FIELD_IPV4,
-		.input_index = RTE_ACL_IPV4VLAN_DST,
-		.offset = offsetof(struct rte_ipv4_hdr, dst_addr) -
-			offsetof(struct rte_ipv4_hdr, next_proto_id),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_RANGE,
-		.size = sizeof(uint16_t),
-		.field_index = SRCP_FIELD_IPV4,
-		.input_index = RTE_ACL_IPV4VLAN_PORTS,
-		.offset = sizeof(struct rte_ipv4_hdr) -
-			offsetof(struct rte_ipv4_hdr, next_proto_id),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_RANGE,
-		.size = sizeof(uint16_t),
-		.field_index = DSTP_FIELD_IPV4,
-		.input_index = RTE_ACL_IPV4VLAN_PORTS,
-		.offset = sizeof(struct rte_ipv4_hdr) -
-			offsetof(struct rte_ipv4_hdr, next_proto_id) +
-			sizeof(uint16_t),
-	},
-};
-
-#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))
-
-enum {
-	PROTO_FIELD_IPV6,
-	SRC1_FIELD_IPV6,
-	SRC2_FIELD_IPV6,
-	SRC3_FIELD_IPV6,
-	SRC4_FIELD_IPV6,
-	DST1_FIELD_IPV6,
-	DST2_FIELD_IPV6,
-	DST3_FIELD_IPV6,
-	DST4_FIELD_IPV6,
-	SRCP_FIELD_IPV6,
-	DSTP_FIELD_IPV6,
-	NUM_FIELDS_IPV6
-};
-
-struct rte_acl_field_def ipv6_defs[NUM_FIELDS_IPV6] = {
-	{
-		.type = RTE_ACL_FIELD_TYPE_BITMASK,
-		.size = sizeof(uint8_t),
-		.field_index = PROTO_FIELD_IPV6,
-		.input_index = PROTO_FIELD_IPV6,
-		.offset = 0,
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = SRC1_FIELD_IPV6,
-		.input_index = SRC1_FIELD_IPV6,
-		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
-			offsetof(struct rte_ipv6_hdr, proto),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = SRC2_FIELD_IPV6,
-		.input_index = SRC2_FIELD_IPV6,
-		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
-			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint32_t),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = SRC3_FIELD_IPV6,
-		.input_index = SRC3_FIELD_IPV6,
-		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
-			offsetof(struct rte_ipv6_hdr, proto) +
-			2 * sizeof(uint32_t),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = SRC4_FIELD_IPV6,
-		.input_index = SRC4_FIELD_IPV6,
-		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
-			offsetof(struct rte_ipv6_hdr, proto) +
-			3 * sizeof(uint32_t),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = DST1_FIELD_IPV6,
-		.input_index = DST1_FIELD_IPV6,
-		.offset = offsetof(struct rte_ipv6_hdr, dst_addr)
-				- offsetof(struct rte_ipv6_hdr, proto),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = DST2_FIELD_IPV6,
-		.input_index = DST2_FIELD_IPV6,
-		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
-			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint32_t),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = DST3_FIELD_IPV6,
-		.input_index = DST3_FIELD_IPV6,
-		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
-			offsetof(struct rte_ipv6_hdr, proto) +
-			2 * sizeof(uint32_t),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_MASK,
-		.size = sizeof(uint32_t),
-		.field_index = DST4_FIELD_IPV6,
-		.input_index = DST4_FIELD_IPV6,
-		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
-			offsetof(struct rte_ipv6_hdr, proto) +
-			3 * sizeof(uint32_t),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_RANGE,
-		.size = sizeof(uint16_t),
-		.field_index = SRCP_FIELD_IPV6,
-		.input_index = SRCP_FIELD_IPV6,
-		.offset = sizeof(struct rte_ipv6_hdr) -
-			offsetof(struct rte_ipv6_hdr, proto),
-	},
-	{
-		.type = RTE_ACL_FIELD_TYPE_RANGE,
-		.size = sizeof(uint16_t),
-		.field_index = DSTP_FIELD_IPV6,
-		.input_index = SRCP_FIELD_IPV6,
-		.offset = sizeof(struct rte_ipv6_hdr) -
-			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint16_t),
-	},
-};
-
-enum {
-	CB_FLD_SRC_ADDR,
-	CB_FLD_DST_ADDR,
-	CB_FLD_SRC_PORT_LOW,
-	CB_FLD_SRC_PORT_DLM,
-	CB_FLD_SRC_PORT_HIGH,
-	CB_FLD_DST_PORT_LOW,
-	CB_FLD_DST_PORT_DLM,
-	CB_FLD_DST_PORT_HIGH,
-	CB_FLD_PROTO,
-	CB_FLD_USERDATA,
-	CB_FLD_NUM,
-};
-
-RTE_ACL_RULE_DEF(acl4_rule, RTE_DIM(ipv4_defs));
-RTE_ACL_RULE_DEF(acl6_rule, RTE_DIM(ipv6_defs));
-
-struct acl_search_t {
-	const uint8_t *data_ipv4[MAX_PKT_BURST];
-	struct rte_mbuf *m_ipv4[MAX_PKT_BURST];
-	uint32_t res_ipv4[MAX_PKT_BURST];
-	int num_ipv4;
-
-	const uint8_t *data_ipv6[MAX_PKT_BURST];
-	struct rte_mbuf *m_ipv6[MAX_PKT_BURST];
-	uint32_t res_ipv6[MAX_PKT_BURST];
-	int num_ipv6;
-};
-
-static struct {
-	char mapped[NB_SOCKETS];
-	struct rte_acl_ctx *acx_ipv4[NB_SOCKETS];
-	struct rte_acl_ctx *acx_ipv6[NB_SOCKETS];
-#ifdef L3FWDACL_DEBUG
-	struct acl4_rule *rule_ipv4;
-	struct acl6_rule *rule_ipv6;
-#endif
-} acl_config;
-
-static struct{
-	const char *rule_ipv4_name;
-	const char *rule_ipv6_name;
-	enum rte_acl_classify_alg alg;
-} parm_config;
-
-const char cb_port_delim[] = ":";
-
-static inline void
-print_one_ipv4_rule(struct acl4_rule *rule, int extra)
-{
-	unsigned char a, b, c, d;
-
-	uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
-			&a, &b, &c, &d);
-	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
-			rule->field[SRC_FIELD_IPV4].mask_range.u32);
-	uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
-			&a, &b, &c, &d);
-	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
-			rule->field[DST_FIELD_IPV4].mask_range.u32);
-	printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
-		rule->field[SRCP_FIELD_IPV4].value.u16,
-		rule->field[SRCP_FIELD_IPV4].mask_range.u16,
-		rule->field[DSTP_FIELD_IPV4].value.u16,
-		rule->field[DSTP_FIELD_IPV4].mask_range.u16,
-		rule->field[PROTO_FIELD_IPV4].value.u8,
-		rule->field[PROTO_FIELD_IPV4].mask_range.u8);
-	if (extra)
-		printf("0x%x-0x%x-0x%x ",
-			rule->data.category_mask,
-			rule->data.priority,
-			rule->data.userdata);
-}
-
-static inline void
-print_one_ipv6_rule(struct acl6_rule *rule, int extra)
-{
-	unsigned char a, b, c, d;
-
-	uint32_t_to_char(rule->field[SRC1_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC2_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC3_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[SRC4_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
-			rule->field[SRC1_FIELD_IPV6].mask_range.u32
-			+ rule->field[SRC2_FIELD_IPV6].mask_range.u32
-			+ rule->field[SRC3_FIELD_IPV6].mask_range.u32
-			+ rule->field[SRC4_FIELD_IPV6].mask_range.u32);
-
-	uint32_t_to_char(rule->field[DST1_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf("%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST2_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST3_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x", a, b, c, d);
-	uint32_t_to_char(rule->field[DST4_FIELD_IPV6].value.u32,
-		&a, &b, &c, &d);
-	printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d,
-			rule->field[DST1_FIELD_IPV6].mask_range.u32
-			+ rule->field[DST2_FIELD_IPV6].mask_range.u32
-			+ rule->field[DST3_FIELD_IPV6].mask_range.u32
-			+ rule->field[DST4_FIELD_IPV6].mask_range.u32);
-
-	printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
-		rule->field[SRCP_FIELD_IPV6].value.u16,
-		rule->field[SRCP_FIELD_IPV6].mask_range.u16,
-		rule->field[DSTP_FIELD_IPV6].value.u16,
-		rule->field[DSTP_FIELD_IPV6].mask_range.u16,
-		rule->field[PROTO_FIELD_IPV6].value.u8,
-		rule->field[PROTO_FIELD_IPV6].mask_range.u8);
-	if (extra)
-		printf("0x%x-0x%x-0x%x ",
-			rule->data.category_mask,
-			rule->data.priority,
-			rule->data.userdata);
-}
-
-/* Bypass comment and empty lines */
-static inline int
-is_bypass_line(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;
-}
-
-#ifdef L3FWDACL_DEBUG
-static inline void
-dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
-{
-	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
-	unsigned char a, b, c, d;
-	struct rte_ipv4_hdr *ipv4_hdr =
-		rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *,
-					sizeof(struct rte_ether_hdr));
-
-	uint32_t_to_char(rte_bswap32(ipv4_hdr->src_addr), &a, &b, &c, &d);
-	printf("Packet Src:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
-	uint32_t_to_char(rte_bswap32(ipv4_hdr->dst_addr), &a, &b, &c, &d);
-	printf("Dst:%hhu.%hhu.%hhu.%hhu ", a, b, c, d);
-
-	printf("Src port:%hu,Dst port:%hu ",
-			rte_bswap16(*(uint16_t *)(ipv4_hdr + 1)),
-			rte_bswap16(*((uint16_t *)(ipv4_hdr + 1) + 1)));
-	printf("hit ACL %d - ", offset);
-
-	print_one_ipv4_rule(acl_config.rule_ipv4 + offset, 1);
-
-	printf("\n\n");
-}
-
-static inline void
-dump_acl6_rule(struct rte_mbuf *m, uint32_t sig)
-{
-	unsigned i;
-	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
-	struct rte_ipv6_hdr *ipv6_hdr =
-		rte_pktmbuf_mtod_offset(m, struct rte_ipv6_hdr *,
-					sizeof(struct rte_ether_hdr));
-
-	printf("Packet Src");
-	for (i = 0; i < RTE_DIM(ipv6_hdr->src_addr); i += sizeof(uint16_t))
-		printf(":%.2x%.2x",
-			ipv6_hdr->src_addr[i], ipv6_hdr->src_addr[i + 1]);
-
-	printf("\nDst");
-	for (i = 0; i < RTE_DIM(ipv6_hdr->dst_addr); i += sizeof(uint16_t))
-		printf(":%.2x%.2x",
-			ipv6_hdr->dst_addr[i], ipv6_hdr->dst_addr[i + 1]);
-
-	printf("\nSrc port:%hu,Dst port:%hu ",
-			rte_bswap16(*(uint16_t *)(ipv6_hdr + 1)),
-			rte_bswap16(*((uint16_t *)(ipv6_hdr + 1) + 1)));
-	printf("hit ACL %d - ", offset);
-
-	print_one_ipv6_rule(acl_config.rule_ipv6 + offset, 1);
-
-	printf("\n\n");
-}
-#endif /* L3FWDACL_DEBUG */
-
-static inline void
-dump_ipv4_rules(struct acl4_rule *rule, int num, int extra)
-{
-	int i;
-
-	for (i = 0; i < num; i++, rule++) {
-		printf("\t%d:", i + 1);
-		print_one_ipv4_rule(rule, extra);
-		printf("\n");
-	}
-}
-
-static inline void
-dump_ipv6_rules(struct acl6_rule *rule, int num, int extra)
-{
-	int i;
-
-	for (i = 0; i < num; i++, rule++) {
-		printf("\t%d:", i + 1);
-		print_one_ipv6_rule(rule, extra);
-		printf("\n");
-	}
-}
-
-#ifdef DO_RFC_1812_CHECKS
-static inline void
-prepare_one_packet(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
-	int index)
-{
-	struct rte_ipv4_hdr *ipv4_hdr;
-	struct rte_mbuf *pkt = pkts_in[index];
-
-	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
-		ipv4_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv4_hdr *,
-						sizeof(struct rte_ether_hdr));
-
-		/* Check to make sure the packet is valid (RFC1812) */
-		if (is_valid_ipv4_pkt(ipv4_hdr, pkt->pkt_len) >= 0) {
-
-			/* Update time to live and header checksum */
-			--(ipv4_hdr->time_to_live);
-			++(ipv4_hdr->hdr_checksum);
-
-			/* Fill acl structure */
-			acl->data_ipv4[acl->num_ipv4] = MBUF_IPV4_2PROTO(pkt);
-			acl->m_ipv4[(acl->num_ipv4)++] = pkt;
-
-		} else {
-			/* Not a valid IPv4 packet */
-			rte_pktmbuf_free(pkt);
-		}
-	} else if (RTE_ETH_IS_IPV6_HDR(pkt->packet_type)) {
-		/* Fill acl structure */
-		acl->data_ipv6[acl->num_ipv6] = MBUF_IPV6_2PROTO(pkt);
-		acl->m_ipv6[(acl->num_ipv6)++] = pkt;
-
-	} else {
-		/* Unknown type, drop the packet */
-		rte_pktmbuf_free(pkt);
-	}
-}
-
-#else
-static inline void
-prepare_one_packet(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
-	int index)
-{
-	struct rte_mbuf *pkt = pkts_in[index];
-
-	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
-		/* Fill acl structure */
-		acl->data_ipv4[acl->num_ipv4] = MBUF_IPV4_2PROTO(pkt);
-		acl->m_ipv4[(acl->num_ipv4)++] = pkt;
-
-	} else if (RTE_ETH_IS_IPV6_HDR(pkt->packet_type)) {
-		/* Fill acl structure */
-		acl->data_ipv6[acl->num_ipv6] = MBUF_IPV6_2PROTO(pkt);
-		acl->m_ipv6[(acl->num_ipv6)++] = pkt;
-	} else {
-		/* Unknown type, drop the packet */
-		rte_pktmbuf_free(pkt);
-	}
-}
-#endif /* DO_RFC_1812_CHECKS */
-
-static inline void
-prepare_acl_parameter(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
-	int nb_rx)
-{
-	int i;
-
-	acl->num_ipv4 = 0;
-	acl->num_ipv6 = 0;
-
-	/* Prefetch first packets */
-	for (i = 0; i < PREFETCH_OFFSET && i < nb_rx; i++) {
-		rte_prefetch0(rte_pktmbuf_mtod(
-				pkts_in[i], void *));
-	}
-
-	for (i = 0; i < (nb_rx - PREFETCH_OFFSET); i++) {
-		rte_prefetch0(rte_pktmbuf_mtod(pkts_in[
-				i + PREFETCH_OFFSET], void *));
-		prepare_one_packet(pkts_in, acl, i);
-	}
-
-	/* Process left packets */
-	for (; i < nb_rx; i++)
-		prepare_one_packet(pkts_in, acl, i);
-}
-
-static inline void
-send_one_packet(struct rte_mbuf *m, uint32_t res)
-{
-	if (likely((res & ACL_DENY_SIGNATURE) == 0 && res != 0)) {
-		/* forward packets */
-		send_single_packet(m,
-			(uint8_t)(res - FWD_PORT_SHIFT));
-	} else{
-		/* in the ACL list, drop it */
-#ifdef L3FWDACL_DEBUG
-		if ((res & ACL_DENY_SIGNATURE) != 0) {
-			if (RTE_ETH_IS_IPV4_HDR(m->packet_type))
-				dump_acl4_rule(m, res);
-			else if (RTE_ETH_IS_IPV6_HDR(m->packet_type))
-				dump_acl6_rule(m, res);
-		}
-#endif
-		rte_pktmbuf_free(m);
-	}
-}
-
-
-
-static inline void
-send_packets(struct rte_mbuf **m, uint32_t *res, int num)
-{
-	int i;
-
-	/* Prefetch first packets */
-	for (i = 0; i < PREFETCH_OFFSET && i < num; i++) {
-		rte_prefetch0(rte_pktmbuf_mtod(
-				m[i], void *));
-	}
-
-	for (i = 0; i < (num - PREFETCH_OFFSET); i++) {
-		rte_prefetch0(rte_pktmbuf_mtod(m[
-				i + PREFETCH_OFFSET], void *));
-		send_one_packet(m[i], res[i]);
-	}
-
-	/* Process left packets */
-	for (; i < num; i++)
-		send_one_packet(m[i], res[i]);
-}
-
-/*
- * Parse IPv6 address, expects the following format:
- * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X is a hexadecimal digit).
- */
-static int
-parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
-	char dlm)
-{
-	uint32_t addr[IPV6_ADDR_U16];
-
-	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
-	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
-
-	*end = in;
-
-	v[0] = (addr[0] << 16) + addr[1];
-	v[1] = (addr[2] << 16) + addr[3];
-	v[2] = (addr[4] << 16) + addr[5];
-	v[3] = (addr[6] << 16) + addr[7];
-
-	return 0;
-}
-
-static int
-parse_ipv6_net(const char *in, struct rte_acl_field field[4])
-{
-	int32_t rc;
-	const char *mp;
-	uint32_t i, m, v[4];
-	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
-
-	/* get address. */
-	rc = parse_ipv6_addr(in, &mp, v, '/');
-	if (rc != 0)
-		return rc;
-
-	/* get mask. */
-	GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0);
-
-	/* put all together. */
-	for (i = 0; i != RTE_DIM(v); i++) {
-		if (m >= (i + 1) * nbu32)
-			field[i].mask_range.u32 = nbu32;
-		else
-			field[i].mask_range.u32 = m > (i * nbu32) ?
-				m - (i * 32) : 0;
-
-		field[i].value.u32 = v[i];
-	}
-
-	return 0;
-}
-
-static int
-parse_cb_ipv6_rule(char *str, struct rte_acl_rule *v, int has_userdata)
-{
-	int i, rc;
-	char *s, *sp, *in[CB_FLD_NUM];
-	static const char *dlm = " \t\n";
-	int dim = has_userdata ? CB_FLD_NUM : CB_FLD_USERDATA;
-	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_ipv6_net(in[CB_FLD_SRC_ADDR], v->field + SRC1_FIELD_IPV6);
-	if (rc != 0) {
-		acl_log("failed to read source address/mask: %s\n",
-			in[CB_FLD_SRC_ADDR]);
-		return rc;
-	}
-
-	rc = parse_ipv6_net(in[CB_FLD_DST_ADDR], v->field + DST1_FIELD_IPV6);
-	if (rc != 0) {
-		acl_log("failed to read destination address/mask: %s\n",
-			in[CB_FLD_DST_ADDR]);
-		return rc;
-	}
-
-	/* source port. */
-	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
-		v->field[SRCP_FIELD_IPV6].value.u16,
-		0, UINT16_MAX, 0);
-	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
-		v->field[SRCP_FIELD_IPV6].mask_range.u16,
-		0, UINT16_MAX, 0);
-
-	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
-			sizeof(cb_port_delim)) != 0)
-		return -EINVAL;
-
-	/* destination port. */
-	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
-		v->field[DSTP_FIELD_IPV6].value.u16,
-		0, UINT16_MAX, 0);
-	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
-		v->field[DSTP_FIELD_IPV6].mask_range.u16,
-		0, UINT16_MAX, 0);
-
-	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
-			sizeof(cb_port_delim)) != 0)
-		return -EINVAL;
-
-	if (v->field[SRCP_FIELD_IPV6].mask_range.u16
-			< v->field[SRCP_FIELD_IPV6].value.u16
-			|| v->field[DSTP_FIELD_IPV6].mask_range.u16
-			< v->field[DSTP_FIELD_IPV6].value.u16)
-		return -EINVAL;
-
-	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].value.u8,
-		0, UINT8_MAX, '/');
-	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].mask_range.u8,
-		0, UINT8_MAX, 0);
-
-	if (has_userdata)
-		GET_CB_FIELD(in[CB_FLD_USERDATA], v->data.userdata,
-			0, UINT32_MAX, 0);
-
-	return 0;
-}
-
-/*
- * Parse ClassBench rules file.
- * Expected format:
- * '@'<src_ipv4_addr>'/'<masklen> <space> \
- * <dst_ipv4_addr>'/'<masklen> <space> \
- * <src_port_low> <space> ":" <src_port_high> <space> \
- * <dst_port_low> <space> ":" <dst_port_high> <space> \
- * <proto>'/'<mask>
- */
-static int
-parse_ipv4_net(const char *in, uint32_t *addr, uint32_t *mask_len)
-{
-	uint8_t a, b, c, d, m;
-
-	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
-	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
-	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
-
-	addr[0] = RTE_IPV4(a, b, c, d);
-	mask_len[0] = m;
-
-	return 0;
-}
-
-static int
-parse_cb_ipv4vlan_rule(char *str, struct rte_acl_rule *v, int has_userdata)
-{
-	int i, rc;
-	char *s, *sp, *in[CB_FLD_NUM];
-	static const char *dlm = " \t\n";
-	int dim = has_userdata ? CB_FLD_NUM : CB_FLD_USERDATA;
-	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_net(in[CB_FLD_SRC_ADDR],
-			&v->field[SRC_FIELD_IPV4].value.u32,
-			&v->field[SRC_FIELD_IPV4].mask_range.u32);
-	if (rc != 0) {
-			acl_log("failed to read source address/mask: %s\n",
-			in[CB_FLD_SRC_ADDR]);
-		return rc;
-	}
-
-	rc = parse_ipv4_net(in[CB_FLD_DST_ADDR],
-			&v->field[DST_FIELD_IPV4].value.u32,
-			&v->field[DST_FIELD_IPV4].mask_range.u32);
-	if (rc != 0) {
-		acl_log("failed to read destination address/mask: %s\n",
-			in[CB_FLD_DST_ADDR]);
-		return rc;
-	}
-
-	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
-		v->field[SRCP_FIELD_IPV4].value.u16,
-		0, UINT16_MAX, 0);
-	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
-		v->field[SRCP_FIELD_IPV4].mask_range.u16,
-		0, UINT16_MAX, 0);
-
-	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
-			sizeof(cb_port_delim)) != 0)
-		return -EINVAL;
-
-	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
-		v->field[DSTP_FIELD_IPV4].value.u16,
-		0, UINT16_MAX, 0);
-	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
-		v->field[DSTP_FIELD_IPV4].mask_range.u16,
-		0, UINT16_MAX, 0);
-
-	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
-			sizeof(cb_port_delim)) != 0)
-		return -EINVAL;
-
-	if (v->field[SRCP_FIELD_IPV4].mask_range.u16
-			< v->field[SRCP_FIELD_IPV4].value.u16
-			|| v->field[DSTP_FIELD_IPV4].mask_range.u16
-			< v->field[DSTP_FIELD_IPV4].value.u16)
-		return -EINVAL;
-
-	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].value.u8,
-		0, UINT8_MAX, '/');
-	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].mask_range.u8,
-		0, UINT8_MAX, 0);
-
-	if (has_userdata)
-		GET_CB_FIELD(in[CB_FLD_USERDATA], v->data.userdata, 0,
-			UINT32_MAX, 0);
-
-	return 0;
-}
-
-static int
-add_rules(const char *rule_path,
-		struct rte_acl_rule **proute_base,
-		unsigned int *proute_num,
-		struct rte_acl_rule **pacl_base,
-		unsigned int *pacl_num, uint32_t rule_size,
-		int (*parser)(char *, struct rte_acl_rule*, int))
-{
-	uint8_t *acl_rules, *route_rules;
-	struct rte_acl_rule *next;
-	unsigned int acl_num = 0, route_num = 0, total_num = 0;
-	unsigned int acl_cnt = 0, route_cnt = 0;
-	char buff[LINE_MAX];
-	FILE *fh = fopen(rule_path, "rb");
-	unsigned int i = 0;
-	int val;
-
-	if (fh == NULL)
-		rte_exit(EXIT_FAILURE, "%s: Open %s failed\n", __func__,
-			rule_path);
-
-	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
-		if (buff[0] == ROUTE_LEAD_CHAR)
-			route_num++;
-		else if (buff[0] == ACL_LEAD_CHAR)
-			acl_num++;
-	}
-
-	if (0 == route_num)
-		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
-				rule_path);
-
-	val = fseek(fh, 0, SEEK_SET);
-	if (val < 0) {
-		rte_exit(EXIT_FAILURE, "%s: File seek operation failed\n",
-			__func__);
-	}
-
-	acl_rules = calloc(acl_num, rule_size);
-
-	if (NULL == acl_rules)
-		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
-			__func__);
-
-	route_rules = calloc(route_num, rule_size);
-
-	if (NULL == route_rules)
-		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
-			__func__);
-
-	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 = (struct rte_acl_rule *)(route_rules +
-				route_cnt * rule_size);
-
-		/* ACL entry */
-		else if (s == ACL_LEAD_CHAR)
-			next = (struct rte_acl_rule *)(acl_rules +
-				acl_cnt * rule_size);
-
-		/* Illegal line */
-		else
-			rte_exit(EXIT_FAILURE,
-				"%s Line %u: should start with leading "
-				"char %c or %c\n",
-				rule_path, i, ROUTE_LEAD_CHAR, ACL_LEAD_CHAR);
-
-		if (parser(buff + 1, next, s == ROUTE_LEAD_CHAR) != 0)
-			rte_exit(EXIT_FAILURE,
-				"%s Line %u: parse rules error\n",
-				rule_path, i);
-
-		if (s == ROUTE_LEAD_CHAR) {
-			/* Check the forwarding port number */
-			if ((enabled_port_mask & (1 << next->data.userdata)) ==
-					0)
-				rte_exit(EXIT_FAILURE,
-					"%s Line %u: fwd number illegal:%u\n",
-					rule_path, i, next->data.userdata);
-			next->data.userdata += FWD_PORT_SHIFT;
-			route_cnt++;
-		} else {
-			next->data.userdata = ACL_DENY_SIGNATURE + acl_cnt;
-			acl_cnt++;
-		}
-
-		next->data.priority = RTE_ACL_MAX_PRIORITY - total_num;
-		next->data.category_mask = -1;
-		total_num++;
-	}
-
-	fclose(fh);
-
-	*pacl_base = (struct rte_acl_rule *)acl_rules;
-	*pacl_num = acl_num;
-	*proute_base = (struct rte_acl_rule *)route_rules;
-	*proute_num = route_cnt;
-
-	return 0;
-}
-
-static int
-usage_acl_alg(char *buf, size_t sz)
-{
-	uint32_t i, n, rc, tn;
-
-	n = 0;
-	tn = 0;
-	for (i = 0; i < RTE_DIM(acl_alg); i++) {
-		rc = snprintf(buf + n, sz - n,
-			i == RTE_DIM(acl_alg) - 1 ? "%s" : "%s|",
-			acl_alg[i].name);
-		tn += rc;
-		if (rc < sz - n)
-			n += rc;
-	}
-
-	return tn;
-}
-
-static const char *
-str_acl_alg(enum rte_acl_classify_alg alg)
-{
-	uint32_t i;
-
-	for (i = 0; i != RTE_DIM(acl_alg); i++) {
-		if (alg == acl_alg[i].alg)
-			return acl_alg[i].name;
-	}
-
-	return "default";
-}
-
-static enum rte_acl_classify_alg
-parse_acl_alg(const char *alg)
-{
-	uint32_t i;
-
-	for (i = 0; i != RTE_DIM(acl_alg); i++) {
-		if (strcmp(alg, acl_alg[i].name) == 0)
-			return acl_alg[i].alg;
-	}
-
-	return RTE_ACL_CLASSIFY_DEFAULT;
-}
-
-static void
-dump_acl_config(void)
-{
-	printf("ACL option are:\n");
-	printf(OPT_RULE_IPV4": %s\n", parm_config.rule_ipv4_name);
-	printf(OPT_RULE_IPV6": %s\n", parm_config.rule_ipv6_name);
-	printf(OPT_ALG": %s\n", str_acl_alg(parm_config.alg));
-}
-
-static int
-check_acl_config(void)
-{
-	if (parm_config.rule_ipv4_name == NULL) {
-		acl_log("ACL IPv4 rule file not specified\n");
-		return -1;
-	} else if (parm_config.rule_ipv6_name == NULL) {
-		acl_log("ACL IPv6 rule file not specified\n");
-		return -1;
-	}
-
-	return 0;
-}
-
-static struct rte_acl_ctx*
-setup_acl(struct rte_acl_rule *route_base,
-		struct rte_acl_rule *acl_base, unsigned int route_num,
-		unsigned int acl_num, int ipv6, int socketid)
-{
-	char name[PATH_MAX];
-	struct rte_acl_param acl_param;
-	struct rte_acl_config acl_build_param;
-	struct rte_acl_ctx *context;
-	int dim = ipv6 ? RTE_DIM(ipv6_defs) : RTE_DIM(ipv4_defs);
-
-	/* Create ACL contexts */
-	snprintf(name, sizeof(name), "%s%d",
-			ipv6 ? L3FWD_ACL_IPV6_NAME : L3FWD_ACL_IPV4_NAME,
-			socketid);
-
-	acl_param.name = name;
-	acl_param.socket_id = socketid;
-	acl_param.rule_size = RTE_ACL_RULE_SZ(dim);
-	acl_param.max_rule_num = MAX_ACL_RULE_NUM;
-
-	if ((context = rte_acl_create(&acl_param)) == NULL)
-		rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
-
-	if (parm_config.alg != RTE_ACL_CLASSIFY_DEFAULT &&
-			rte_acl_set_ctx_classify(context, parm_config.alg) != 0)
-		rte_exit(EXIT_FAILURE,
-			"Failed to setup classify method for  ACL context\n");
-
-	if (rte_acl_add_rules(context, route_base, route_num) < 0)
-			rte_exit(EXIT_FAILURE, "add rules failed\n");
-
-	if (rte_acl_add_rules(context, acl_base, acl_num) < 0)
-			rte_exit(EXIT_FAILURE, "add rules failed\n");
-
-	/* Perform builds */
-	memset(&acl_build_param, 0, sizeof(acl_build_param));
-
-	acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
-	acl_build_param.num_fields = dim;
-	memcpy(&acl_build_param.defs, ipv6 ? ipv6_defs : ipv4_defs,
-		ipv6 ? sizeof(ipv6_defs) : sizeof(ipv4_defs));
-
-	if (rte_acl_build(context, &acl_build_param) != 0)
-		rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");
-
-	rte_acl_dump(context);
-
-	return context;
-}
-
-static int
-app_acl_init(void)
-{
-	unsigned lcore_id;
-	unsigned int i;
-	int socketid;
-	struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
-		*acl_base_ipv6, *route_base_ipv6;
-	unsigned int acl_num_ipv4 = 0, route_num_ipv4 = 0,
-		acl_num_ipv6 = 0, route_num_ipv6 = 0;
-
-	if (check_acl_config() != 0)
-		rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
-
-	dump_acl_config();
-
-	/* Load  rules from the input file */
-	if (add_rules(parm_config.rule_ipv4_name, &route_base_ipv4,
-			&route_num_ipv4, &acl_base_ipv4, &acl_num_ipv4,
-			sizeof(struct acl4_rule), &parse_cb_ipv4vlan_rule) < 0)
-		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
-
-	acl_log("IPv4 Route entries %u:\n", route_num_ipv4);
-	dump_ipv4_rules((struct acl4_rule *)route_base_ipv4, route_num_ipv4, 1);
-
-	acl_log("IPv4 ACL entries %u:\n", acl_num_ipv4);
-	dump_ipv4_rules((struct acl4_rule *)acl_base_ipv4, acl_num_ipv4, 1);
-
-	if (add_rules(parm_config.rule_ipv6_name, &route_base_ipv6,
-			&route_num_ipv6,
-			&acl_base_ipv6, &acl_num_ipv6,
-			sizeof(struct acl6_rule), &parse_cb_ipv6_rule) < 0)
-		rte_exit(EXIT_FAILURE, "Failed to add rules\n");
-
-	acl_log("IPv6 Route entries %u:\n", route_num_ipv6);
-	dump_ipv6_rules((struct acl6_rule *)route_base_ipv6, route_num_ipv6, 1);
-
-	acl_log("IPv6 ACL entries %u:\n", acl_num_ipv6);
-	dump_ipv6_rules((struct acl6_rule *)acl_base_ipv6, acl_num_ipv6, 1);
-
-	memset(&acl_config, 0, sizeof(acl_config));
-
-	/* Check sockets a context should be created on */
-	if (!numa_on)
-		acl_config.mapped[0] = 1;
-	else {
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			socketid = rte_lcore_to_socket_id(lcore_id);
-			if (socketid >= NB_SOCKETS) {
-				acl_log("Socket %d of lcore %u is out "
-					"of range %d\n",
-					socketid, lcore_id, NB_SOCKETS);
-				free(route_base_ipv4);
-				free(route_base_ipv6);
-				free(acl_base_ipv4);
-				free(acl_base_ipv6);
-				return -1;
-			}
-
-			acl_config.mapped[socketid] = 1;
-		}
-	}
-
-	for (i = 0; i < NB_SOCKETS; i++) {
-		if (acl_config.mapped[i]) {
-			acl_config.acx_ipv4[i] = setup_acl(route_base_ipv4,
-				acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
-				0, i);
-
-			acl_config.acx_ipv6[i] = setup_acl(route_base_ipv6,
-				acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
-				1, i);
-		}
-	}
-
-	free(route_base_ipv4);
-	free(route_base_ipv6);
-
-#ifdef L3FWDACL_DEBUG
-	acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
-	acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
-#else
-	free(acl_base_ipv4);
-	free(acl_base_ipv6);
-#endif
-
-	return 0;
-}
-
-/***********************end of ACL part******************************/
-
-struct lcore_conf {
-	uint16_t n_rx_queue;
-	struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE];
-	uint16_t n_tx_port;
-	uint16_t tx_port_id[RTE_MAX_ETHPORTS];
-	uint16_t tx_queue_id[RTE_MAX_ETHPORTS];
-	struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
-} __rte_cache_aligned;
-
-static struct lcore_conf lcore_conf[RTE_MAX_LCORE];
-
-/* Enqueue a single packet, and send burst if queue is filled */
-static inline void
-send_single_packet(struct rte_mbuf *m, uint16_t port)
-{
-	uint32_t lcore_id;
-	struct lcore_conf *qconf;
-	struct rte_ether_hdr *eh;
-
-	lcore_id = rte_lcore_id();
-
-	/* update src and dst mac*/
-	eh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
-	memcpy(eh, &port_l2hdr[port],
-			sizeof(eh->dst_addr) + sizeof(eh->src_addr));
-
-	qconf = &lcore_conf[lcore_id];
-	rte_eth_tx_buffer(port, qconf->tx_queue_id[port],
-			qconf->tx_buffer[port], m);
-}
-
-#ifdef DO_RFC_1812_CHECKS
-static inline int
-is_valid_ipv4_pkt(struct rte_ipv4_hdr *pkt, uint32_t link_len)
-{
-	/* From http://www.rfc-editor.org/rfc/rfc1812.txt section 5.2.2 */
-	/*
-	 * 1. The packet length reported by the Link Layer must be large
-	 * enough to hold the minimum length legal IP datagram (20 bytes).
-	 */
-	if (link_len < sizeof(struct rte_ipv4_hdr))
-		return -1;
-
-	/* 2. The IP checksum must be correct. */
-	/* this is checked in H/W */
-
-	/*
-	 * 3. The IP version number must be 4. If the version number is not 4
-	 * then the packet may be another version of IP, such as IPng or
-	 * ST-II.
-	 */
-	if (((pkt->version_ihl) >> 4) != 4)
-		return -3;
-	/*
-	 * 4. The IP header length field must be large enough to hold the
-	 * minimum length legal IP datagram (20 bytes = 5 words).
-	 */
-	if ((pkt->version_ihl & 0xf) < 5)
-		return -4;
-
-	/*
-	 * 5. The IP total length field must be large enough to hold the IP
-	 * datagram header, whose length is specified in the IP header length
-	 * field.
-	 */
-	if (rte_cpu_to_be_16(pkt->total_length) < sizeof(struct rte_ipv4_hdr))
-		return -5;
-
-	return 0;
-}
-#endif
-
-/* main processing loop */
-static int
-main_loop(__rte_unused void *dummy)
-{
-	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
-	unsigned lcore_id;
-	uint64_t prev_tsc, diff_tsc, cur_tsc;
-	int i, nb_rx;
-	uint16_t portid;
-	uint8_t queueid;
-	struct lcore_conf *qconf;
-	int socketid;
-	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1)
-			/ US_PER_S * BURST_TX_DRAIN_US;
-
-	prev_tsc = 0;
-	lcore_id = rte_lcore_id();
-	qconf = &lcore_conf[lcore_id];
-	socketid = rte_lcore_to_socket_id(lcore_id);
-
-	if (qconf->n_rx_queue == 0) {
-		RTE_LOG(INFO, L3FWD, "lcore %u has nothing to do\n", lcore_id);
-		return 0;
-	}
-
-	RTE_LOG(INFO, L3FWD, "entering main loop on lcore %u\n", lcore_id);
-
-	for (i = 0; i < qconf->n_rx_queue; i++) {
-
-		portid = qconf->rx_queue_list[i].port_id;
-		queueid = qconf->rx_queue_list[i].queue_id;
-		RTE_LOG(INFO, L3FWD,
-			" -- lcoreid=%u portid=%u rxqueueid=%hhu\n",
-			lcore_id, portid, queueid);
-	}
-
-	while (1) {
-
-		cur_tsc = rte_rdtsc();
-
-		/*
-		 * TX burst queue drain
-		 */
-		diff_tsc = cur_tsc - prev_tsc;
-		if (unlikely(diff_tsc > drain_tsc)) {
-			for (i = 0; i < qconf->n_tx_port; ++i) {
-				portid = qconf->tx_port_id[i];
-				rte_eth_tx_buffer_flush(portid,
-						qconf->tx_queue_id[portid],
-						qconf->tx_buffer[portid]);
-			}
-			prev_tsc = cur_tsc;
-		}
-
-		/*
-		 * Read packet from RX queues
-		 */
-		for (i = 0; i < qconf->n_rx_queue; ++i) {
-
-			portid = qconf->rx_queue_list[i].port_id;
-			queueid = qconf->rx_queue_list[i].queue_id;
-			nb_rx = rte_eth_rx_burst(portid, queueid,
-				pkts_burst, MAX_PKT_BURST);
-
-			if (nb_rx > 0) {
-				struct acl_search_t acl_search;
-
-				prepare_acl_parameter(pkts_burst, &acl_search,
-					nb_rx);
-
-				if (acl_search.num_ipv4) {
-					rte_acl_classify(
-						acl_config.acx_ipv4[socketid],
-						acl_search.data_ipv4,
-						acl_search.res_ipv4,
-						acl_search.num_ipv4,
-						DEFAULT_MAX_CATEGORIES);
-
-					send_packets(acl_search.m_ipv4,
-						acl_search.res_ipv4,
-						acl_search.num_ipv4);
-				}
-
-				if (acl_search.num_ipv6) {
-					rte_acl_classify(
-						acl_config.acx_ipv6[socketid],
-						acl_search.data_ipv6,
-						acl_search.res_ipv6,
-						acl_search.num_ipv6,
-						DEFAULT_MAX_CATEGORIES);
-
-					send_packets(acl_search.m_ipv6,
-						acl_search.res_ipv6,
-						acl_search.num_ipv6);
-				}
-			}
-		}
-	}
-}
-
-static int
-check_lcore_params(void)
-{
-	uint8_t queue, lcore;
-	uint16_t i;
-	int socketid;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		queue = lcore_params[i].queue_id;
-		if (queue >= MAX_RX_QUEUE_PER_PORT) {
-			printf("invalid queue number: %hhu\n", queue);
-			return -1;
-		}
-		lcore = lcore_params[i].lcore_id;
-		if (!rte_lcore_is_enabled(lcore)) {
-			printf("error: lcore %hhu is not enabled in "
-				"lcore mask\n", lcore);
-			return -1;
-		}
-		socketid = rte_lcore_to_socket_id(lcore);
-		if (socketid != 0 && numa_on == 0) {
-			printf("warning: lcore %hhu is on socket %d "
-				"with numa off\n",
-				lcore, socketid);
-		}
-	}
-	return 0;
-}
-
-static int
-check_port_config(void)
-{
-	unsigned portid;
-	uint16_t i;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		portid = lcore_params[i].port_id;
-
-		if ((enabled_port_mask & (1 << portid)) == 0) {
-			printf("port %u is not enabled in port mask\n", portid);
-			return -1;
-		}
-		if (!rte_eth_dev_is_valid_port(portid)) {
-			printf("port %u is not present on the board\n", portid);
-			return -1;
-		}
-	}
-	return 0;
-}
-
-static uint8_t
-get_port_n_rx_queues(const uint16_t port)
-{
-	int queue = -1;
-	uint16_t i;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		if (lcore_params[i].port_id == port &&
-				lcore_params[i].queue_id > queue)
-			queue = lcore_params[i].queue_id;
-	}
-	return (uint8_t)(++queue);
-}
-
-static int
-init_lcore_rx_queues(void)
-{
-	uint16_t i, nb_rx_queue;
-	uint8_t lcore;
-
-	for (i = 0; i < nb_lcore_params; ++i) {
-		lcore = lcore_params[i].lcore_id;
-		nb_rx_queue = lcore_conf[lcore].n_rx_queue;
-		if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) {
-			printf("error: too many queues (%u) for lcore: %u\n",
-				(unsigned)nb_rx_queue + 1, (unsigned)lcore);
-			return -1;
-		} else {
-			lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id =
-				lcore_params[i].port_id;
-			lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id =
-				lcore_params[i].queue_id;
-			lcore_conf[lcore].n_rx_queue++;
-		}
-	}
-	return 0;
-}
-
-/* display usage */
-static void
-print_usage(const char *prgname)
-{
-	char alg[PATH_MAX];
-
-	usage_acl_alg(alg, sizeof(alg));
-	printf("%s [EAL options] -- -p PORTMASK -P"
-		"  --"OPT_RULE_IPV4"=FILE"
-		"  --"OPT_RULE_IPV6"=FILE"
-		"  [--"OPT_CONFIG" (port,queue,lcore)[,(port,queue,lcore]]"
-		"  [--"OPT_MAX_PKT_LEN" PKTLEN]\n"
-		"  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
-		"  -P: enable promiscuous mode\n"
-		"  --"OPT_CONFIG" (port,queue,lcore): rx queues configuration\n"
-		"  --"OPT_NONUMA": optional, disable numa awareness\n"
-		"  --"OPT_MAX_PKT_LEN" PKTLEN: maximum packet length in decimal (64-9600)\n"
-		"  --"OPT_RULE_IPV4"=FILE: specify the ipv4 rules entries file. "
-		"Each rule occupy one line. "
-		"2 kinds of rules are supported. "
-		"One is ACL entry at while line leads with character '%c', "
-		"another is route entry at while line leads with character '%c'.\n"
-		"  --"OPT_RULE_IPV6"=FILE: specify the ipv6 rules entries file.\n"
-		"  --"OPT_ALG": ACL classify method to use, one of: %s\n",
-		prgname, ACL_LEAD_CHAR, ROUTE_LEAD_CHAR, alg);
-}
-
-static int
-parse_max_pkt_len(const char *pktlen)
-{
-	char *end = NULL;
-	unsigned long len;
-
-	/* parse decimal string */
-	len = strtoul(pktlen, &end, 10);
-	if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0'))
-		return -1;
-
-	if (len == 0)
-		return -1;
-
-	return len;
-}
-
-static int
-parse_portmask(const char *portmask)
-{
-	char *end = NULL;
-	unsigned long pm;
-
-	/* parse hexadecimal string */
-	pm = strtoul(portmask, &end, 16);
-	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
-		return 0;
-
-	return pm;
-}
-
-static int
-parse_config(const char *q_arg)
-{
-	char s[256];
-	const char *p, *p0 = q_arg;
-	char *end;
-	enum fieldnames {
-		FLD_PORT = 0,
-		FLD_QUEUE,
-		FLD_LCORE,
-		_NUM_FLD
-	};
-	unsigned long int_fld[_NUM_FLD];
-	char *str_fld[_NUM_FLD];
-	int i;
-	unsigned size;
-
-	nb_lcore_params = 0;
-
-	while ((p = strchr(p0, '(')) != NULL) {
-		++p;
-		if ((p0 = strchr(p, ')')) == NULL)
-			return -1;
-
-		size = p0 - p;
-		if (size >= sizeof(s))
-			return -1;
-
-		snprintf(s, sizeof(s), "%.*s", size, p);
-		if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') !=
-				_NUM_FLD)
-			return -1;
-		for (i = 0; i < _NUM_FLD; i++) {
-			errno = 0;
-			int_fld[i] = strtoul(str_fld[i], &end, 0);
-			if (errno != 0 || end == str_fld[i] || int_fld[i] > 255)
-				return -1;
-		}
-		if (nb_lcore_params >= MAX_LCORE_PARAMS) {
-			printf("exceeded max number of lcore params: %hu\n",
-				nb_lcore_params);
-			return -1;
-		}
-		lcore_params_array[nb_lcore_params].port_id =
-			(uint8_t)int_fld[FLD_PORT];
-		lcore_params_array[nb_lcore_params].queue_id =
-			(uint8_t)int_fld[FLD_QUEUE];
-		lcore_params_array[nb_lcore_params].lcore_id =
-			(uint8_t)int_fld[FLD_LCORE];
-		++nb_lcore_params;
-	}
-	lcore_params = lcore_params_array;
-	return 0;
-}
-
-static const char *
-parse_eth_dest(const char *optarg)
-{
-	unsigned long portid;
-	char *port_end;
-
-	errno = 0;
-	portid = strtoul(optarg, &port_end, 0);
-	if (errno != 0 || port_end == optarg || *port_end++ != ',')
-		return "Invalid format";
-	else if (portid >= RTE_MAX_ETHPORTS)
-		return "port value exceeds RTE_MAX_ETHPORTS("
-			RTE_STR(RTE_MAX_ETHPORTS) ")";
-
-	if (cmdline_parse_etheraddr(NULL, port_end,
-			&port_l2hdr[portid].dst_addr,
-			sizeof(port_l2hdr[portid].dst_addr)) < 0)
-		return "Invalid ethernet address";
-	return NULL;
-}
-
-/* Parse the argument given in the command line of the application */
-static int
-parse_args(int argc, char **argv)
-{
-	int opt, ret;
-	char **argvopt;
-	int option_index;
-	char *prgname = argv[0];
-	static struct option lgopts[] = {
-		{OPT_CONFIG,      1, NULL, OPT_CONFIG_NUM      },
-		{OPT_NONUMA,      0, NULL, OPT_NONUMA_NUM      },
-		{OPT_MAX_PKT_LEN, 1, NULL, OPT_MAX_PKT_LEN_NUM },
-		{OPT_RULE_IPV4,   1, NULL, OPT_RULE_IPV4_NUM   },
-		{OPT_RULE_IPV6,   1, NULL, OPT_RULE_IPV6_NUM   },
-		{OPT_ALG,         1, NULL, OPT_ALG_NUM         },
-		{OPT_ETH_DEST,    1, NULL, OPT_ETH_DEST_NUM    },
-		{NULL,            0, 0,    0                   }
-	};
-
-	argvopt = argv;
-
-	while ((opt = getopt_long(argc, argvopt, "p:P",
-				lgopts, &option_index)) != EOF) {
-
-		switch (opt) {
-		/* portmask */
-		case 'p':
-			enabled_port_mask = parse_portmask(optarg);
-			if (enabled_port_mask == 0) {
-				printf("invalid portmask\n");
-				print_usage(prgname);
-				return -1;
-			}
-			break;
-
-		case 'P':
-			printf("Promiscuous mode selected\n");
-			promiscuous_on = 1;
-			break;
-
-		/* long options */
-		case OPT_CONFIG_NUM:
-			ret = parse_config(optarg);
-			if (ret) {
-				printf("invalid config\n");
-				print_usage(prgname);
-				return -1;
-			}
-			break;
-
-		case OPT_NONUMA_NUM:
-			printf("numa is disabled\n");
-			numa_on = 0;
-			break;
-
-		case OPT_MAX_PKT_LEN_NUM:
-			printf("Custom frame size is configured\n");
-			max_pkt_len = parse_max_pkt_len(optarg);
-			break;
-
-		case OPT_RULE_IPV4_NUM:
-			parm_config.rule_ipv4_name = optarg;
-			break;
-
-		case OPT_RULE_IPV6_NUM:
-			parm_config.rule_ipv6_name = optarg;
-			break;
-
-		case OPT_ALG_NUM:
-			parm_config.alg = parse_acl_alg(optarg);
-			if (parm_config.alg ==
-					RTE_ACL_CLASSIFY_DEFAULT) {
-				printf("unknown %s value:\"%s\"\n",
-					OPT_ALG, optarg);
-				print_usage(prgname);
-				return -1;
-			}
-			break;
-
-		case OPT_ETH_DEST_NUM:
-		{
-			const char *serr = parse_eth_dest(optarg);
-			if (serr != NULL) {
-				printf("invalid %s value:\"%s\": %s\n",
-					OPT_ETH_DEST, optarg, serr);
-				print_usage(prgname);
-				return -1;
-			}
-			break;
-		}
-		default:
-			print_usage(prgname);
-			return -1;
-		}
-	}
-
-	if (optind >= 0)
-		argv[optind-1] = prgname;
-
-	ret = optind-1;
-	optind = 1; /* reset getopt lib */
-	return ret;
-}
-
-static void
-print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
-{
-	char buf[RTE_ETHER_ADDR_FMT_SIZE];
-	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr);
-	printf("%s%s", name, buf);
-}
-
-static int
-init_mem(unsigned nb_mbuf)
-{
-	int socketid;
-	unsigned lcore_id;
-	char s[64];
-
-	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-		if (rte_lcore_is_enabled(lcore_id) == 0)
-			continue;
-
-		if (numa_on)
-			socketid = rte_lcore_to_socket_id(lcore_id);
-		else
-			socketid = 0;
-
-		if (socketid >= NB_SOCKETS) {
-			rte_exit(EXIT_FAILURE,
-				"Socket %d of lcore %u is out of range %d\n",
-				socketid, lcore_id, NB_SOCKETS);
-		}
-		if (pktmbuf_pool[socketid] == NULL) {
-			snprintf(s, sizeof(s), "mbuf_pool_%d", socketid);
-			pktmbuf_pool[socketid] =
-				rte_pktmbuf_pool_create(s, nb_mbuf,
-					MEMPOOL_CACHE_SIZE, 0,
-					RTE_MBUF_DEFAULT_BUF_SIZE,
-					socketid);
-			if (pktmbuf_pool[socketid] == NULL)
-				rte_exit(EXIT_FAILURE,
-					"Cannot init mbuf pool on socket %d\n",
-					socketid);
-			else
-				printf("Allocated mbuf pool on socket %d\n",
-					socketid);
-		}
-	}
-	return 0;
-}
-
-/* Check the link status of all ports in up to 9s, and print them finally */
-static void
-check_all_ports_link_status(uint32_t port_mask)
-{
-#define CHECK_INTERVAL 100 /* 100ms */
-#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
-	uint16_t portid;
-	uint8_t count, all_ports_up, print_flag = 0;
-	struct rte_eth_link link;
-	int ret;
-	char link_status_text[RTE_ETH_LINK_MAX_STR_LEN];
-
-	printf("\nChecking link status");
-	fflush(stdout);
-	for (count = 0; count <= MAX_CHECK_TIME; count++) {
-		all_ports_up = 1;
-		RTE_ETH_FOREACH_DEV(portid) {
-			if ((port_mask & (1 << portid)) == 0)
-				continue;
-			memset(&link, 0, sizeof(link));
-			ret = rte_eth_link_get_nowait(portid, &link);
-			if (ret < 0) {
-				all_ports_up = 0;
-				if (print_flag == 1)
-					printf("Port %u link get failed: %s\n",
-						portid, rte_strerror(-ret));
-				continue;
-			}
-			/* print link status if flag set */
-			if (print_flag == 1) {
-				rte_eth_link_to_str(link_status_text,
-					sizeof(link_status_text), &link);
-				printf("Port %d %s\n", portid,
-				       link_status_text);
-				continue;
-			}
-			/* clear all_ports_up flag if any link down */
-			if (link.link_status == RTE_ETH_LINK_DOWN) {
-				all_ports_up = 0;
-				break;
-			}
-		}
-		/* after finally printing all link status, get out */
-		if (print_flag == 1)
-			break;
-
-		if (all_ports_up == 0) {
-			printf(".");
-			fflush(stdout);
-			rte_delay_ms(CHECK_INTERVAL);
-		}
-
-		/* set the print_flag if all ports up or timeout */
-		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
-			print_flag = 1;
-			printf("done\n");
-		}
-	}
-}
-
-/*
- * build-up default values for dest MACs.
- */
-static void
-set_default_dest_mac(void)
-{
-	uint32_t i;
-
-	for (i = 0; i != RTE_DIM(port_l2hdr); i++) {
-		port_l2hdr[i].dst_addr.addr_bytes[0] =
-				RTE_ETHER_LOCAL_ADMIN_ADDR;
-		port_l2hdr[i].dst_addr.addr_bytes[5] = i;
-	}
-}
-
-static uint32_t
-eth_dev_get_overhead_len(uint32_t max_rx_pktlen, uint16_t max_mtu)
-{
-	uint32_t overhead_len;
-
-	if (max_mtu != UINT16_MAX && max_rx_pktlen > max_mtu)
-		overhead_len = max_rx_pktlen - max_mtu;
-	else
-		overhead_len = RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN;
-
-	return overhead_len;
-}
-
-static int
-config_port_max_pkt_len(struct rte_eth_conf *conf,
-		struct rte_eth_dev_info *dev_info)
-{
-	uint32_t overhead_len;
-
-	if (max_pkt_len == 0)
-		return 0;
-
-	if (max_pkt_len < RTE_ETHER_MIN_LEN || max_pkt_len > MAX_JUMBO_PKT_LEN)
-		return -1;
-
-	overhead_len = eth_dev_get_overhead_len(dev_info->max_rx_pktlen,
-			dev_info->max_mtu);
-	conf->rxmode.mtu = max_pkt_len - overhead_len;
-
-	if (conf->rxmode.mtu > RTE_ETHER_MTU)
-		conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_MULTI_SEGS;
-
-	return 0;
-}
-
-int
-main(int argc, char **argv)
-{
-	struct lcore_conf *qconf;
-	struct rte_eth_dev_info dev_info;
-	struct rte_eth_txconf *txconf;
-	int ret;
-	unsigned nb_ports;
-	uint16_t queueid;
-	unsigned lcore_id;
-	uint32_t n_tx_queue, nb_lcores;
-	uint16_t portid;
-	uint8_t nb_rx_queue, queue, socketid;
-
-	/* init EAL */
-	ret = rte_eal_init(argc, argv);
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
-	argc -= ret;
-	argv += ret;
-
-	set_default_dest_mac();
-
-	/* parse application arguments (after the EAL ones) */
-	ret = parse_args(argc, argv);
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n");
-
-	if (check_lcore_params() < 0)
-		rte_exit(EXIT_FAILURE, "check_lcore_params failed\n");
-
-	ret = init_lcore_rx_queues();
-	if (ret < 0)
-		rte_exit(EXIT_FAILURE, "init_lcore_rx_queues failed\n");
-
-	nb_ports = rte_eth_dev_count_avail();
-
-	if (check_port_config() < 0)
-		rte_exit(EXIT_FAILURE, "check_port_config failed\n");
-
-	/* Add ACL rules and route entries, build trie */
-	if (app_acl_init() < 0)
-		rte_exit(EXIT_FAILURE, "app_acl_init failed\n");
-
-	nb_lcores = rte_lcore_count();
-
-	/* initialize all ports */
-	RTE_ETH_FOREACH_DEV(portid) {
-		struct rte_eth_conf local_port_conf = port_conf;
-
-		/* skip ports that are not enabled */
-		if ((enabled_port_mask & (1 << portid)) == 0) {
-			printf("\nSkipping disabled port %d\n", portid);
-			continue;
-		}
-
-		/* init port */
-		printf("Initializing port %d ... ", portid);
-		fflush(stdout);
-
-		nb_rx_queue = get_port_n_rx_queues(portid);
-		n_tx_queue = nb_lcores;
-		if (n_tx_queue > MAX_TX_QUEUE_PER_PORT)
-			n_tx_queue = MAX_TX_QUEUE_PER_PORT;
-		printf("Creating queues: nb_rxq=%d nb_txq=%u... ",
-			nb_rx_queue, (unsigned)n_tx_queue);
-
-		ret = rte_eth_dev_info_get(portid, &dev_info);
-		if (ret != 0)
-			rte_exit(EXIT_FAILURE,
-				"Error during getting device (port %u) info: %s\n",
-				portid, strerror(-ret));
-
-		ret = config_port_max_pkt_len(&local_port_conf, &dev_info);
-		if (ret != 0)
-			rte_exit(EXIT_FAILURE,
-				"Invalid max packet length: %u (port %u)\n",
-				max_pkt_len, portid);
-
-		if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE)
-			local_port_conf.txmode.offloads |=
-				RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE;
-
-		local_port_conf.rx_adv_conf.rss_conf.rss_hf &=
-			dev_info.flow_type_rss_offloads;
-		if (local_port_conf.rx_adv_conf.rss_conf.rss_hf !=
-				port_conf.rx_adv_conf.rss_conf.rss_hf) {
-			printf("Port %u modified RSS hash function based on hardware support,"
-				"requested:%#"PRIx64" configured:%#"PRIx64"\n",
-				portid,
-				port_conf.rx_adv_conf.rss_conf.rss_hf,
-				local_port_conf.rx_adv_conf.rss_conf.rss_hf);
-		}
-
-		ret = rte_eth_dev_configure(portid, nb_rx_queue,
-					(uint16_t)n_tx_queue, &local_port_conf);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE,
-				"Cannot configure device: err=%d, port=%d\n",
-				ret, portid);
-
-		ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
-						       &nb_txd);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE,
-				"rte_eth_dev_adjust_nb_rx_tx_desc: err=%d, port=%d\n",
-				ret, portid);
-
-		ret = rte_eth_macaddr_get(portid, &port_l2hdr[portid].src_addr);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE,
-				"rte_eth_macaddr_get: err=%d, port=%d\n",
-				ret, portid);
-
-		print_ethaddr("Dst MAC:", &port_l2hdr[portid].dst_addr);
-		print_ethaddr(", Src MAC:", &port_l2hdr[portid].src_addr);
-		printf(", ");
-
-		/* init memory */
-		ret = init_mem(NB_MBUF);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE, "init_mem failed\n");
-
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			/* Initialize TX buffers */
-			qconf = &lcore_conf[lcore_id];
-			qconf->tx_buffer[portid] = rte_zmalloc_socket("tx_buffer",
-					RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,
-					rte_eth_dev_socket_id(portid));
-			if (qconf->tx_buffer[portid] == NULL)
-				rte_exit(EXIT_FAILURE, "Can't allocate tx buffer for port %u\n",
-						(unsigned) portid);
-
-			rte_eth_tx_buffer_init(qconf->tx_buffer[portid], MAX_PKT_BURST);
-		}
-
-		/* init one TX queue per couple (lcore,port) */
-		queueid = 0;
-		for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-			if (rte_lcore_is_enabled(lcore_id) == 0)
-				continue;
-
-			if (numa_on)
-				socketid = (uint8_t)
-					rte_lcore_to_socket_id(lcore_id);
-			else
-				socketid = 0;
-
-			printf("txq=%u,%d,%d ", lcore_id, queueid, socketid);
-			fflush(stdout);
-
-			ret = rte_eth_dev_info_get(portid, &dev_info);
-			if (ret != 0)
-				rte_exit(EXIT_FAILURE,
-					"Error during getting device (port %u) info: %s\n",
-					portid, strerror(-ret));
-
-			txconf = &dev_info.default_txconf;
-			txconf->offloads = local_port_conf.txmode.offloads;
-			ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd,
-						     socketid, txconf);
-			if (ret < 0)
-				rte_exit(EXIT_FAILURE,
-					"rte_eth_tx_queue_setup: err=%d, "
-					"port=%d\n", ret, portid);
-
-			qconf = &lcore_conf[lcore_id];
-			qconf->tx_queue_id[portid] = queueid;
-			queueid++;
-
-			qconf->tx_port_id[qconf->n_tx_port] = portid;
-			qconf->n_tx_port++;
-		}
-		printf("\n");
-	}
-
-	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
-		if (rte_lcore_is_enabled(lcore_id) == 0)
-			continue;
-		qconf = &lcore_conf[lcore_id];
-		printf("\nInitializing rx queues on lcore %u ... ", lcore_id);
-		fflush(stdout);
-		/* init RX queues */
-		for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
-			struct rte_eth_rxconf rxq_conf;
-
-			portid = qconf->rx_queue_list[queue].port_id;
-			queueid = qconf->rx_queue_list[queue].queue_id;
-
-			if (numa_on)
-				socketid = (uint8_t)
-					rte_lcore_to_socket_id(lcore_id);
-			else
-				socketid = 0;
-
-			printf("rxq=%d,%d,%d ", portid, queueid, socketid);
-			fflush(stdout);
-
-			ret = rte_eth_dev_info_get(portid, &dev_info);
-			if (ret != 0)
-				rte_exit(EXIT_FAILURE,
-					"Error during getting device (port %u) info: %s\n",
-					portid, strerror(-ret));
-
-			rxq_conf = dev_info.default_rxconf;
-			rxq_conf.offloads = port_conf.rxmode.offloads;
-			ret = rte_eth_rx_queue_setup(portid, queueid, nb_rxd,
-					socketid, &rxq_conf,
-					pktmbuf_pool[socketid]);
-			if (ret < 0)
-				rte_exit(EXIT_FAILURE,
-					"rte_eth_rx_queue_setup: err=%d,"
-					"port=%d\n", ret, portid);
-		}
-	}
-
-	printf("\n");
-
-	/* start ports */
-	RTE_ETH_FOREACH_DEV(portid) {
-		if ((enabled_port_mask & (1 << portid)) == 0)
-			continue;
-
-		/* Start device */
-		ret = rte_eth_dev_start(portid);
-		if (ret < 0)
-			rte_exit(EXIT_FAILURE,
-				"rte_eth_dev_start: err=%d, port=%d\n",
-				ret, portid);
-
-		/*
-		 * If enabled, put device in promiscuous mode.
-		 * This allows IO forwarding mode to forward packets
-		 * to itself through 2 cross-connected  ports of the
-		 * target machine.
-		 */
-		if (promiscuous_on) {
-			ret = rte_eth_promiscuous_enable(portid);
-			if (ret != 0)
-				rte_exit(EXIT_FAILURE,
-					"rte_eth_promiscuous_enable: err=%s, port=%u\n",
-					rte_strerror(-ret), portid);
-		}
-	}
-
-	check_all_ports_link_status(enabled_port_mask);
-
-	/* launch per-lcore init on every lcore */
-	rte_eal_mp_remote_launch(main_loop, NULL, CALL_MAIN);
-	RTE_LCORE_FOREACH_WORKER(lcore_id) {
-		if (rte_eal_wait_lcore(lcore_id) < 0)
-			return -1;
-	}
-
-	/* clean up the EAL */
-	rte_eal_cleanup();
-
-	return 0;
-}
diff --git a/examples/l3fwd-acl/meson.build b/examples/l3fwd-acl/meson.build
deleted file mode 100644
index 6efe46e710..0000000000
--- a/examples/l3fwd-acl/meson.build
+++ /dev/null
@@ -1,13 +0,0 @@ 
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2017 Intel Corporation
-
-# meson file, for building this example as part of a main DPDK build.
-#
-# To build this example as a standalone application with an already-installed
-# DPDK instance, use 'make'
-
-allow_experimental_apis = true
-deps += ['acl', 'lpm', 'hash']
-sources = files(
-        'main.c',
-)
diff --git a/examples/l3fwd/Makefile b/examples/l3fwd/Makefile
index 0badedb499..8efe6378e2 100644
--- a/examples/l3fwd/Makefile
+++ b/examples/l3fwd/Makefile
@@ -5,7 +5,7 @@ 
 APP = l3fwd
 
 # all source are stored in SRCS-y
-SRCS-y := main.c l3fwd_lpm.c l3fwd_fib.c l3fwd_em.c l3fwd_event.c
+SRCS-y := main.c l3fwd_acl.c l3fwd_lpm.c l3fwd_fib.c l3fwd_em.c l3fwd_event.c
 SRCS-y += l3fwd_event_generic.c l3fwd_event_internal_port.c
 
 PKGCONF ?= pkg-config
diff --git a/examples/l3fwd/l3fwd.h b/examples/l3fwd/l3fwd.h
index ad39496e64..8a52c90755 100644
--- a/examples/l3fwd/l3fwd.h
+++ b/examples/l3fwd/l3fwd.h
@@ -7,6 +7,7 @@ 
 
 #include <rte_ethdev.h>
 #include <rte_vect.h>
+#include <rte_acl.h>
 
 #define DO_RFC_1812_CHECKS
 
@@ -61,6 +62,12 @@ 
 struct parm_cfg {
 	const char *rule_ipv4_name;
 	const char *rule_ipv6_name;
+	enum rte_acl_classify_alg alg;
+};
+
+struct acl_algorithms {
+	const char *name;
+	enum rte_acl_classify_alg alg;
 };
 
 struct mbuf_table {
@@ -107,6 +114,8 @@  extern struct lcore_conf lcore_conf[RTE_MAX_LCORE];
 
 extern struct parm_cfg parm_config;
 
+extern struct acl_algorithms acl_alg[];
+
 /* Send burst of packets on an output interface */
 static inline int
 send_burst(struct lcore_conf *qconf, uint16_t n, uint16_t port)
@@ -190,10 +199,19 @@  is_valid_ipv4_pkt(struct rte_ipv4_hdr *pkt, uint32_t link_len)
 }
 #endif /* DO_RFC_1812_CHECKS */
 
+enum rte_acl_classify_alg
+parse_acl_alg(const char *alg);
+
+int
+usage_acl_alg(char *buf, size_t sz);
+
 int
 init_mem(uint16_t portid, unsigned int nb_mbuf);
 
-/* Function pointers for LPM, EM or FIB functionality. */
+/* Function pointers for ACL, LPM, EM or FIB functionality. */
+void
+setup_acl(const int socketid);
+
 void
 setup_lpm(const int socketid);
 
@@ -217,6 +235,9 @@  uint16_t
 lpm_cb_parse_ptype(uint16_t port, uint16_t queue, struct rte_mbuf *pkts[],
 		   uint16_t nb_pkts, uint16_t max_pkts, void *user_param);
 
+int
+acl_main_loop(__rte_unused void *dummy);
+
 int
 em_main_loop(__rte_unused void *dummy);
 
@@ -278,7 +299,13 @@  int
 fib_event_main_loop_tx_q_burst_vector(__rte_unused void *dummy);
 
 
-/* Return ipv4/ipv6 fwd lookup struct for LPM, EM or FIB. */
+/* Return ipv4/ipv6 fwd lookup struct for ACL, LPM, EM or FIB. */
+void *
+acl_get_ipv4_l3fwd_lookup_struct(const int socketid);
+
+void *
+acl_get_ipv6_l3fwd_lookup_struct(const int socketid);
+
 void *
 em_get_ipv4_l3fwd_lookup_struct(const int socketid);
 
diff --git a/examples/l3fwd/l3fwd_acl.c b/examples/l3fwd/l3fwd_acl.c
new file mode 100644
index 0000000000..e5fe38dba5
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl.c
@@ -0,0 +1,1098 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+
+#include "l3fwd.h"
+#include "l3fwd_route.h"
+
+/*
+ * Rule and trace formats definitions.
+ */
+
+enum {
+	PROTO_FIELD_IPV4,
+	SRC_FIELD_IPV4,
+	DST_FIELD_IPV4,
+	SRCP_FIELD_IPV4,
+	DSTP_FIELD_IPV4,
+	NUM_FIELDS_IPV4
+};
+
+/*
+ * That effectively defines order of IPV4VLAN classifications:
+ *  - PROTO
+ *  - VLAN (TAG and DOMAIN)
+ *  - SRC IP ADDRESS
+ *  - DST IP ADDRESS
+ *  - PORTS (SRC and DST)
+ */
+enum {
+	RTE_ACL_IPV4VLAN_PROTO,
+	RTE_ACL_IPV4VLAN_VLAN,
+	RTE_ACL_IPV4VLAN_SRC,
+	RTE_ACL_IPV4VLAN_DST,
+	RTE_ACL_IPV4VLAN_PORTS,
+	RTE_ACL_IPV4VLAN_NUM
+};
+
+struct acl_algorithms acl_alg[] = {
+	{
+		.name = "scalar",
+		.alg = RTE_ACL_CLASSIFY_SCALAR,
+	},
+	{
+		.name = "sse",
+		.alg = RTE_ACL_CLASSIFY_SSE,
+	},
+	{
+		.name = "avx2",
+		.alg = RTE_ACL_CLASSIFY_AVX2,
+	},
+	{
+		.name = "neon",
+		.alg = RTE_ACL_CLASSIFY_NEON,
+	},
+	{
+		.name = "altivec",
+		.alg = RTE_ACL_CLASSIFY_ALTIVEC,
+	},
+	{
+		.name = "avx512x16",
+		.alg = RTE_ACL_CLASSIFY_AVX512X16,
+	},
+	{
+		.name = "avx512x32",
+		.alg = RTE_ACL_CLASSIFY_AVX512X32,
+	},
+};
+
+struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
+	{
+		.type = RTE_ACL_FIELD_TYPE_BITMASK,
+		.size = sizeof(uint8_t),
+		.field_index = PROTO_FIELD_IPV4,
+		.input_index = RTE_ACL_IPV4VLAN_PROTO,
+		.offset = 0,
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = SRC_FIELD_IPV4,
+		.input_index = RTE_ACL_IPV4VLAN_SRC,
+		.offset = offsetof(struct rte_ipv4_hdr, src_addr) -
+			offsetof(struct rte_ipv4_hdr, next_proto_id),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = DST_FIELD_IPV4,
+		.input_index = RTE_ACL_IPV4VLAN_DST,
+		.offset = offsetof(struct rte_ipv4_hdr, dst_addr) -
+			offsetof(struct rte_ipv4_hdr, next_proto_id),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = SRCP_FIELD_IPV4,
+		.input_index = RTE_ACL_IPV4VLAN_PORTS,
+		.offset = sizeof(struct rte_ipv4_hdr) -
+			offsetof(struct rte_ipv4_hdr, next_proto_id),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = DSTP_FIELD_IPV4,
+		.input_index = RTE_ACL_IPV4VLAN_PORTS,
+		.offset = sizeof(struct rte_ipv4_hdr) -
+			offsetof(struct rte_ipv4_hdr, next_proto_id) +
+			sizeof(uint16_t),
+	},
+};
+
+enum {
+	PROTO_FIELD_IPV6,
+	SRC1_FIELD_IPV6,
+	SRC2_FIELD_IPV6,
+	SRC3_FIELD_IPV6,
+	SRC4_FIELD_IPV6,
+	DST1_FIELD_IPV6,
+	DST2_FIELD_IPV6,
+	DST3_FIELD_IPV6,
+	DST4_FIELD_IPV6,
+	SRCP_FIELD_IPV6,
+	DSTP_FIELD_IPV6,
+	NUM_FIELDS_IPV6
+};
+
+struct rte_acl_field_def ipv6_defs[NUM_FIELDS_IPV6] = {
+	{
+		.type = RTE_ACL_FIELD_TYPE_BITMASK,
+		.size = sizeof(uint8_t),
+		.field_index = PROTO_FIELD_IPV6,
+		.input_index = PROTO_FIELD_IPV6,
+		.offset = 0,
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = SRC1_FIELD_IPV6,
+		.input_index = SRC1_FIELD_IPV6,
+		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
+			offsetof(struct rte_ipv6_hdr, proto),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = SRC2_FIELD_IPV6,
+		.input_index = SRC2_FIELD_IPV6,
+		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
+			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint32_t),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = SRC3_FIELD_IPV6,
+		.input_index = SRC3_FIELD_IPV6,
+		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
+			offsetof(struct rte_ipv6_hdr, proto) +
+			2 * sizeof(uint32_t),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = SRC4_FIELD_IPV6,
+		.input_index = SRC4_FIELD_IPV6,
+		.offset = offsetof(struct rte_ipv6_hdr, src_addr) -
+			offsetof(struct rte_ipv6_hdr, proto) +
+			3 * sizeof(uint32_t),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = DST1_FIELD_IPV6,
+		.input_index = DST1_FIELD_IPV6,
+		.offset = offsetof(struct rte_ipv6_hdr, dst_addr)
+				- offsetof(struct rte_ipv6_hdr, proto),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = DST2_FIELD_IPV6,
+		.input_index = DST2_FIELD_IPV6,
+		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
+			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint32_t),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = DST3_FIELD_IPV6,
+		.input_index = DST3_FIELD_IPV6,
+		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
+			offsetof(struct rte_ipv6_hdr, proto) +
+			2 * sizeof(uint32_t),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_MASK,
+		.size = sizeof(uint32_t),
+		.field_index = DST4_FIELD_IPV6,
+		.input_index = DST4_FIELD_IPV6,
+		.offset = offsetof(struct rte_ipv6_hdr, dst_addr) -
+			offsetof(struct rte_ipv6_hdr, proto) +
+			3 * sizeof(uint32_t),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = SRCP_FIELD_IPV6,
+		.input_index = SRCP_FIELD_IPV6,
+		.offset = sizeof(struct rte_ipv6_hdr) -
+			offsetof(struct rte_ipv6_hdr, proto),
+	},
+	{
+		.type = RTE_ACL_FIELD_TYPE_RANGE,
+		.size = sizeof(uint16_t),
+		.field_index = DSTP_FIELD_IPV6,
+		.input_index = SRCP_FIELD_IPV6,
+		.offset = sizeof(struct rte_ipv6_hdr) -
+			offsetof(struct rte_ipv6_hdr, proto) + sizeof(uint16_t),
+	},
+};
+
+enum {
+	CB_FLD_SRC_ADDR,
+	CB_FLD_DST_ADDR,
+	CB_FLD_SRC_PORT_LOW,
+	CB_FLD_SRC_PORT_DLM,
+	CB_FLD_SRC_PORT_HIGH,
+	CB_FLD_DST_PORT_LOW,
+	CB_FLD_DST_PORT_DLM,
+	CB_FLD_DST_PORT_HIGH,
+	CB_FLD_PROTO,
+	CB_FLD_USERDATA,
+	CB_FLD_NUM,
+};
+
+RTE_ACL_RULE_DEF(acl4_rule, RTE_DIM(ipv4_defs));
+RTE_ACL_RULE_DEF(acl6_rule, RTE_DIM(ipv6_defs));
+
+struct acl_search_t {
+	const uint8_t *data_ipv4[MAX_PKT_BURST];
+	struct rte_mbuf *m_ipv4[MAX_PKT_BURST];
+	uint32_t res_ipv4[MAX_PKT_BURST];
+	int num_ipv4;
+
+	const uint8_t *data_ipv6[MAX_PKT_BURST];
+	struct rte_mbuf *m_ipv6[MAX_PKT_BURST];
+	uint32_t res_ipv6[MAX_PKT_BURST];
+	int num_ipv6;
+};
+
+static struct {
+	struct rte_acl_ctx *acx_ipv4[NB_SOCKETS];
+	struct rte_acl_ctx *acx_ipv6[NB_SOCKETS];
+#ifdef L3FWDACL_DEBUG
+	struct acl4_rule *rule_ipv4;
+	struct acl6_rule *rule_ipv6;
+#endif
+} acl_config;
+
+static const char cb_port_delim[] = ":";
+
+static struct rte_acl_rule *acl_base_ipv4, *route_base_ipv4,
+		*acl_base_ipv6, *route_base_ipv6;
+static unsigned int acl_num_ipv4, route_num_ipv4,
+		acl_num_ipv6, route_num_ipv6;
+
+#include "l3fwd_acl.h"
+
+#include "l3fwd_acl_scalar.h"
+
+/*
+ * Parse IPV6 address, expects the following format:
+ * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X is a hexadecimal digit).
+ */
+static int
+parse_ipv6_addr(char *in, uint32_t v[IPV6_ADDR_U32], uint32_t *mask)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(in, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET6, sa, v) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask, 0, 128, 0);
+	return 0;
+}
+
+static int
+parse_ipv6_net(char *in, struct rte_acl_field field[4])
+{
+	int32_t rc;
+	uint32_t i, v[4];
+	uint32_t m;
+	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
+
+	/* get address. */
+	rc = parse_ipv6_addr(in, v, &m);
+	if (rc != 0)
+		return rc;
+
+	/* put all together. */
+	for (i = 0; i != RTE_DIM(v); i++) {
+		if (m >= (i + 1) * nbu32)
+			field[i].mask_range.u32 = nbu32;
+		else
+			field[i].mask_range.u32 = m > (i * nbu32) ?
+				m - (i * 32) : 0;
+
+		field[i].value.u32 = v[i];
+	}
+
+	return 0;
+}
+
+static int
+parse_cb_ipv6_rule(char *str, struct rte_acl_rule *v, int has_userdata)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_NUM];
+	static const char *dlm = " \t\n";
+	int dim = has_userdata ? CB_FLD_NUM : CB_FLD_USERDATA;
+	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_ipv6_net(in[CB_FLD_SRC_ADDR], v->field + SRC1_FIELD_IPV6);
+	if (rc != 0) {
+		acl_log("failed to read source address/mask: %s\n",
+			in[CB_FLD_SRC_ADDR]);
+		return rc;
+	}
+
+	rc = parse_ipv6_net(in[CB_FLD_DST_ADDR], v->field + DST1_FIELD_IPV6);
+	if (rc != 0) {
+		acl_log("failed to read destination address/mask: %s\n",
+			in[CB_FLD_DST_ADDR]);
+		return rc;
+	}
+
+	/* source port. */
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
+		v->field[SRCP_FIELD_IPV6].value.u16,
+		0, UINT16_MAX, 0);
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
+		v->field[SRCP_FIELD_IPV6].mask_range.u16,
+		0, UINT16_MAX, 0);
+
+	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
+			sizeof(cb_port_delim)) != 0)
+		return -EINVAL;
+
+	/* destination port. */
+	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
+		v->field[DSTP_FIELD_IPV6].value.u16,
+		0, UINT16_MAX, 0);
+	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
+		v->field[DSTP_FIELD_IPV6].mask_range.u16,
+		0, UINT16_MAX, 0);
+
+	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
+			sizeof(cb_port_delim)) != 0)
+		return -EINVAL;
+
+	if (v->field[SRCP_FIELD_IPV6].mask_range.u16
+			< v->field[SRCP_FIELD_IPV6].value.u16
+			|| v->field[DSTP_FIELD_IPV6].mask_range.u16
+			< v->field[DSTP_FIELD_IPV6].value.u16)
+		return -EINVAL;
+
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].value.u8,
+		0, UINT8_MAX, '/');
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].mask_range.u8,
+		0, UINT8_MAX, 0);
+
+	if (has_userdata)
+		GET_CB_FIELD(in[CB_FLD_USERDATA], v->data.userdata,
+			0, UINT32_MAX, 0);
+
+	return 0;
+}
+
+/*
+ * Parse ClassBench rules file.
+ * Expected format:
+ * '@'<src_ipv4_addr>'/'<masklen> <space> \
+ * <dst_ipv4_addr>'/'<masklen> <space> \
+ * <src_port_low> <space> ":" <src_port_high> <space> \
+ * <dst_port_low> <space> ":" <dst_port_high> <space> \
+ * <proto>'/'<mask>
+ */
+static int
+parse_ipv4_net(char *in, uint32_t *addr, uint32_t *mask_len)
+{
+	char *sa, *sm, *sv;
+	const char *dlm =  "/";
+
+	sv = NULL;
+	sa = strtok_r(in, dlm, &sv);
+	if (sa == NULL)
+		return -EINVAL;
+	sm = strtok_r(NULL, dlm, &sv);
+	if (sm == NULL)
+		return -EINVAL;
+
+	if (inet_pton(AF_INET, sa, addr) != 1)
+		return -EINVAL;
+
+	GET_CB_FIELD(sm, *mask_len, 0, 32, 0);
+	*addr = ntohl(*addr);
+	return 0;
+}
+
+static int
+parse_cb_ipv4vlan_rule(char *str, struct rte_acl_rule *v, int has_userdata)
+{
+	int i, rc;
+	char *s, *sp, *in[CB_FLD_NUM];
+	static const char *dlm = " \t\n";
+	int dim = has_userdata ? CB_FLD_NUM : CB_FLD_USERDATA;
+	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_net(in[CB_FLD_SRC_ADDR],
+			&v->field[SRC_FIELD_IPV4].value.u32,
+			&v->field[SRC_FIELD_IPV4].mask_range.u32);
+	if (rc != 0) {
+		acl_log("failed to read source address/mask: %s\n",
+			in[CB_FLD_SRC_ADDR]);
+		return rc;
+	}
+
+	rc = parse_ipv4_net(in[CB_FLD_DST_ADDR],
+			&v->field[DST_FIELD_IPV4].value.u32,
+			&v->field[DST_FIELD_IPV4].mask_range.u32);
+	if (rc != 0) {
+		acl_log("failed to read destination address/mask: %s\n",
+			in[CB_FLD_DST_ADDR]);
+		return rc;
+	}
+
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
+		v->field[SRCP_FIELD_IPV4].value.u16,
+		0, UINT16_MAX, 0);
+	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
+		v->field[SRCP_FIELD_IPV4].mask_range.u16,
+		0, UINT16_MAX, 0);
+
+	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
+			sizeof(cb_port_delim)) != 0) {
+		return -EINVAL;
+	}
+
+	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
+		v->field[DSTP_FIELD_IPV4].value.u16,
+		0, UINT16_MAX, 0);
+	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
+		v->field[DSTP_FIELD_IPV4].mask_range.u16,
+		0, UINT16_MAX, 0);
+
+	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
+			sizeof(cb_port_delim)) != 0) {
+		return -EINVAL;
+	}
+
+	if (v->field[SRCP_FIELD_IPV4].mask_range.u16
+			< v->field[SRCP_FIELD_IPV4].value.u16
+			|| v->field[DSTP_FIELD_IPV4].mask_range.u16
+			< v->field[DSTP_FIELD_IPV4].value.u16) {
+		return -EINVAL;
+	}
+
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].value.u8,
+		0, UINT8_MAX, '/');
+	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].mask_range.u8,
+		0, UINT8_MAX, 0);
+
+	if (has_userdata)
+		GET_CB_FIELD(in[CB_FLD_USERDATA], v->data.userdata, 0,
+			UINT32_MAX, 0);
+
+	return 0;
+}
+
+static int
+acl_add_rules(const char *rule_path,
+		struct rte_acl_rule **proute_base,
+		unsigned int *proute_num,
+		struct rte_acl_rule **pacl_base,
+		unsigned int *pacl_num, uint32_t rule_size,
+		int (*parser)(char *, struct rte_acl_rule*, int))
+{
+	uint8_t *acl_rules, *route_rules;
+	struct rte_acl_rule *next;
+	unsigned int acl_num = 0, route_num = 0, total_num = 0;
+	unsigned int acl_cnt = 0, route_cnt = 0;
+	char buff[LINE_MAX];
+	FILE *fh = fopen(rule_path, "rb");
+	unsigned int i = 0;
+	int val;
+
+	if (fh == NULL)
+		rte_exit(EXIT_FAILURE, "%s: Open %s failed\n", __func__,
+			rule_path);
+
+	while ((fgets(buff, LINE_MAX, fh) != NULL)) {
+		if (buff[0] == ROUTE_LEAD_CHAR)
+			route_num++;
+		else if (buff[0] == ACL_LEAD_CHAR)
+			acl_num++;
+	}
+
+	if (route_num == 0)
+		rte_exit(EXIT_FAILURE, "Not find any route entries in %s!\n",
+				rule_path);
+
+	val = fseek(fh, 0, SEEK_SET);
+	if (val < 0) {
+		rte_exit(EXIT_FAILURE, "%s: File seek operation failed\n",
+			__func__);
+	}
+
+	acl_rules = calloc(acl_num, rule_size);
+
+	if (acl_rules == NULL)
+		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
+			__func__);
+
+	route_rules = calloc(route_num, rule_size);
+
+	if (route_rules == NULL)
+		rte_exit(EXIT_FAILURE, "%s: failed to malloc memory\n",
+			__func__);
+
+	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 = (struct rte_acl_rule *)(route_rules +
+				route_cnt * rule_size);
+
+		/* ACL entry */
+		else if (s == ACL_LEAD_CHAR)
+			next = (struct rte_acl_rule *)(acl_rules +
+				acl_cnt * rule_size);
+
+		/* Illegal line */
+		else
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: should start with leading "
+				"char %c or %c\n",
+				rule_path, i, ROUTE_LEAD_CHAR, ACL_LEAD_CHAR);
+
+		if (parser(buff + 1, next, s == ROUTE_LEAD_CHAR) != 0)
+			rte_exit(EXIT_FAILURE,
+				"%s Line %u: parse rules error\n",
+				rule_path, i);
+
+		if (s == ROUTE_LEAD_CHAR) {
+			/* Check the forwarding port number */
+			if ((enabled_port_mask & (1 << next->data.userdata)) ==
+					0)
+				rte_exit(EXIT_FAILURE,
+					"%s Line %u: fwd number illegal:%u\n",
+					rule_path, i, next->data.userdata);
+			next->data.userdata += FWD_PORT_SHIFT;
+			route_cnt++;
+		} else {
+			next->data.userdata = ACL_DENY_SIGNATURE + acl_cnt;
+			acl_cnt++;
+		}
+
+		next->data.priority = RTE_ACL_MAX_PRIORITY - total_num;
+		next->data.category_mask = -1;
+		total_num++;
+	}
+
+	fclose(fh);
+
+	*pacl_base = (struct rte_acl_rule *)acl_rules;
+	*pacl_num = acl_num;
+	*proute_base = (struct rte_acl_rule *)route_rules;
+	*proute_num = route_cnt;
+
+	return 0;
+}
+
+enum rte_acl_classify_alg
+parse_acl_alg(const char *alg)
+{
+	uint32_t i;
+
+	for (i = 0; i != RTE_DIM(acl_alg); i++) {
+		if (strcmp(alg, acl_alg[i].name) == 0)
+			return acl_alg[i].alg;
+	}
+
+	return RTE_ACL_CLASSIFY_DEFAULT;
+}
+
+int
+usage_acl_alg(char *buf, size_t sz)
+{
+	uint32_t i, n, rc, tn;
+
+	n = 0;
+	tn = 0;
+	for (i = 0; i < RTE_DIM(acl_alg); i++) {
+		rc = snprintf(buf + n, sz - n,
+			i == RTE_DIM(acl_alg) - 1 ? "%s" : "%s|",
+			acl_alg[i].name);
+		tn += rc;
+		if (rc < sz - n)
+			n += rc;
+	}
+
+	return tn;
+}
+
+static const char *
+str_acl_alg(enum rte_acl_classify_alg alg)
+{
+	uint32_t i;
+
+	for (i = 0; i != RTE_DIM(acl_alg); i++) {
+		if (alg == acl_alg[i].alg)
+			return acl_alg[i].name;
+	}
+
+	return "default";
+}
+
+static void
+dump_acl_config(void)
+{
+	printf("ACL options are:\n");
+	printf("rule_ipv4: %s\n", parm_config.rule_ipv4_name);
+	printf("rule_ipv6: %s\n", parm_config.rule_ipv6_name);
+	printf("alg: %s\n", str_acl_alg(parm_config.alg));
+}
+
+static int
+check_acl_config(void)
+{
+	if (parm_config.rule_ipv4_name == NULL) {
+		acl_log("ACL IPv4 rule file not specified\n");
+		return -1;
+	} else if (parm_config.rule_ipv6_name == NULL) {
+		acl_log("ACL IPv6 rule file not specified\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Setup ACL context. 8< */
+static struct rte_acl_ctx*
+app_acl_init(struct rte_acl_rule *route_base,
+		struct rte_acl_rule *acl_base, unsigned int route_num,
+		unsigned int acl_num, int ipv6, int socketid)
+{
+	char name[PATH_MAX];
+	struct rte_acl_param acl_param;
+	struct rte_acl_config acl_build_param;
+	struct rte_acl_ctx *context;
+	int dim = ipv6 ? RTE_DIM(ipv6_defs) : RTE_DIM(ipv4_defs);
+
+	/* Create ACL contexts */
+	snprintf(name, sizeof(name), "%s%d",
+			ipv6 ? L3FWD_ACL_IPV6_NAME : L3FWD_ACL_IPV4_NAME,
+			socketid);
+
+	acl_param.name = name;
+	acl_param.socket_id = socketid;
+	acl_param.rule_size = RTE_ACL_RULE_SZ(dim);
+	acl_param.max_rule_num = MAX_ACL_RULE_NUM;
+
+	context = rte_acl_create(&acl_param);
+	if (context == NULL)
+		rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
+
+	if (parm_config.alg != RTE_ACL_CLASSIFY_DEFAULT &&
+			rte_acl_set_ctx_classify(context, parm_config.alg) != 0)
+		rte_exit(EXIT_FAILURE,
+			"Failed to setup classify method for  ACL context\n");
+
+	if (rte_acl_add_rules(context, route_base, route_num) < 0)
+		rte_exit(EXIT_FAILURE, "add rules failed\n");
+
+	if (rte_acl_add_rules(context, acl_base, acl_num) < 0)
+		rte_exit(EXIT_FAILURE, "add rules failed\n");
+
+	/* Perform builds */
+	memset(&acl_build_param, 0, sizeof(acl_build_param));
+
+	acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
+	acl_build_param.num_fields = dim;
+	memcpy(&acl_build_param.defs, ipv6 ? ipv6_defs : ipv4_defs,
+		ipv6 ? sizeof(ipv6_defs) : sizeof(ipv4_defs));
+
+	if (rte_acl_build(context, &acl_build_param) != 0)
+		rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");
+
+	rte_acl_dump(context);
+
+	return context;
+}
+/* >8 End of ACL context setup. */
+
+void
+acl_free_routes(void)
+{
+	free(route_base_ipv4);
+	free(route_base_ipv6);
+	route_base_ipv4 = NULL;
+	route_base_ipv6 = NULL;
+	route_num_ipv4 = 0;
+	route_num_ipv6 = 0;
+	free(acl_base_ipv4);
+	free(acl_base_ipv6);
+	acl_base_ipv4 = NULL;
+	acl_base_ipv6 = NULL;
+	acl_num_ipv4 = 0;
+	acl_num_ipv6 = 0;
+}
+
+/* Load rules from the input file */
+void
+read_config_files_acl(void)
+{
+	/* ipv4 check */
+	if (parm_config.rule_ipv4_name != NULL) {
+		if (acl_add_rules(parm_config.rule_ipv4_name, &route_base_ipv4,
+				&route_num_ipv4, &acl_base_ipv4, &acl_num_ipv4,
+				sizeof(struct acl4_rule), &parse_cb_ipv4vlan_rule) < 0) {
+			acl_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) {
+		if (acl_add_rules(parm_config.rule_ipv6_name, &route_base_ipv6,
+				&route_num_ipv6,
+				&acl_base_ipv6, &acl_num_ipv6,
+				sizeof(struct acl6_rule), &parse_cb_ipv6_rule) < 0) {
+			acl_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
+print_one_ipv4_rule(struct acl4_rule *rule, int extra)
+{
+	char abuf[INET6_ADDRSTRLEN];
+	uint32_t ipv4_addr;
+	ipv4_addr = ntohl(rule->field[SRC_FIELD_IPV4].value.u32);
+	printf("%s/%u ", inet_ntop(AF_INET,
+			&(ipv4_addr), abuf,
+			sizeof(abuf)), rule->field[SRC_FIELD_IPV4].mask_range.u32);
+	ipv4_addr = ntohl(rule->field[DST_FIELD_IPV4].value.u32);
+	printf("%s/%u ", inet_ntop(AF_INET,
+			&(ipv4_addr), abuf,
+			sizeof(abuf)), rule->field[DST_FIELD_IPV4].mask_range.u32);
+	printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
+		rule->field[SRCP_FIELD_IPV4].value.u16,
+		rule->field[SRCP_FIELD_IPV4].mask_range.u16,
+		rule->field[DSTP_FIELD_IPV4].value.u16,
+		rule->field[DSTP_FIELD_IPV4].mask_range.u16,
+		rule->field[PROTO_FIELD_IPV4].value.u8,
+		rule->field[PROTO_FIELD_IPV4].mask_range.u8);
+	if (extra)
+		printf("0x%x-0x%x-0x%x ",
+			rule->data.category_mask,
+			rule->data.priority,
+			rule->data.userdata);
+}
+
+void
+print_one_ipv6_rule(struct acl6_rule *rule, int extra)
+{
+	char abuf[INET6_ADDRSTRLEN];
+	uint32_t ipv6_addr[4];
+
+	ipv6_addr[0] = rule->field[SRC1_FIELD_IPV6].value.u32;
+	ipv6_addr[1] = rule->field[SRC2_FIELD_IPV6].value.u32;
+	ipv6_addr[2] = rule->field[SRC3_FIELD_IPV6].value.u32;
+	ipv6_addr[3] = rule->field[SRC4_FIELD_IPV6].value.u32;
+
+	printf("%s", inet_ntop(AF_INET6, ipv6_addr,
+		abuf, sizeof(abuf)));
+	printf("/%u ",
+			rule->field[SRC1_FIELD_IPV6].mask_range.u32
+			+ rule->field[SRC2_FIELD_IPV6].mask_range.u32
+			+ rule->field[SRC3_FIELD_IPV6].mask_range.u32
+			+ rule->field[SRC4_FIELD_IPV6].mask_range.u32);
+
+	ipv6_addr[0] = rule->field[DST1_FIELD_IPV6].value.u32;
+	ipv6_addr[1] = rule->field[DST2_FIELD_IPV6].value.u32;
+	ipv6_addr[2] = rule->field[DST3_FIELD_IPV6].value.u32;
+	ipv6_addr[3] = rule->field[DST4_FIELD_IPV6].value.u32;
+
+	printf("%s", inet_ntop(AF_INET6, ipv6_addr,
+		abuf, sizeof(abuf)));
+	printf("/%u ",
+			rule->field[DST1_FIELD_IPV6].mask_range.u32
+			+ rule->field[DST2_FIELD_IPV6].mask_range.u32
+			+ rule->field[DST3_FIELD_IPV6].mask_range.u32
+			+ rule->field[DST4_FIELD_IPV6].mask_range.u32);
+
+	printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
+		rule->field[SRCP_FIELD_IPV6].value.u16,
+		rule->field[SRCP_FIELD_IPV6].mask_range.u16,
+		rule->field[DSTP_FIELD_IPV6].value.u16,
+		rule->field[DSTP_FIELD_IPV6].mask_range.u16,
+		rule->field[PROTO_FIELD_IPV6].value.u8,
+		rule->field[PROTO_FIELD_IPV6].mask_range.u8);
+	if (extra)
+		printf("0x%x-0x%x-0x%x ",
+			rule->data.category_mask,
+			rule->data.priority,
+			rule->data.userdata);
+}
+
+#ifdef L3FWDACL_DEBUG
+static inline void
+dump_acl4_rule(struct rte_mbuf *m, uint32_t sig)
+{
+	char abuf[INET6_ADDRSTRLEN];
+	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
+	struct rte_ipv4_hdr *ipv4_hdr =
+		rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *,
+					sizeof(struct rte_ether_hdr));
+
+	printf("Packet Src:%s ", inet_ntop(AF_INET, ipv4_hdr->src_addr,
+		abuf, sizeof(abuf)));
+	printf("Dst:%s ", inet_ntop(AF_INET, ipv4_hdr->dst_addr,
+		abuf, sizeof(abuf)));
+
+	printf("Src port:%hu,Dst port:%hu ",
+			rte_bswap16(*(uint16_t *)(ipv4_hdr + 1)),
+			rte_bswap16(*((uint16_t *)(ipv4_hdr + 1) + 1)));
+	printf("hit ACL %d - ", offset);
+
+	print_one_ipv4_rule(acl_config.rule_ipv4 + offset, 1);
+
+	printf("\n\n");
+}
+
+static inline void
+dump_acl6_rule(struct rte_mbuf *m, uint32_t sig)
+{
+	char abuf[INET6_ADDRSTRLEN];
+	uint32_t offset = sig & ~ACL_DENY_SIGNATURE;
+	struct rte_ipv6_hdr *ipv6_hdr =
+		rte_pktmbuf_mtod_offset(m, struct rte_ipv6_hdr *,
+					sizeof(struct rte_ether_hdr));
+
+	printf("Packet Src");
+	printf("%s", inet_ntop(AF_INET6, ipv6_hdr->src_addr,
+		abuf, sizeof(abuf)));
+	printf("\nDst");
+	printf("%s", inet_ntop(AF_INET6, ipv6_hdr->dst_addr,
+		abuf, sizeof(abuf)));
+
+	printf("\nSrc port:%hu,Dst port:%hu ",
+			rte_bswap16(*(uint16_t *)(ipv6_hdr + 1)),
+			rte_bswap16(*((uint16_t *)(ipv6_hdr + 1) + 1)));
+	printf("hit ACL %d - ", offset);
+
+	print_one_ipv6_rule(acl_config.rule_ipv6 + offset, 1);
+
+	printf("\n\n");
+}
+#endif /* L3FWDACL_DEBUG */
+
+static inline void
+dump_ipv4_rules(struct acl4_rule *rule, int num, int extra)
+{
+	int i;
+
+	for (i = 0; i < num; i++, rule++) {
+		printf("\t%d:", i + 1);
+		print_one_ipv4_rule(rule, extra);
+		printf("\n");
+	}
+}
+
+static inline void
+dump_ipv6_rules(struct acl6_rule *rule, int num, int extra)
+{
+	int i;
+
+	for (i = 0; i < num; i++, rule++) {
+		printf("\t%d:", i + 1);
+		print_one_ipv6_rule(rule, extra);
+		printf("\n");
+	}
+}
+
+/* Function to setup acl. */
+void
+setup_acl(const int socket_id)
+{
+	if (check_acl_config() != 0)
+		rte_exit(EXIT_FAILURE, "Failed to get valid ACL options\n");
+
+	dump_acl_config();
+
+	acl_log("IPv4 Route entries %u:\n", route_num_ipv4);
+	dump_ipv4_rules((struct acl4_rule *)route_base_ipv4, route_num_ipv4, 1);
+
+	acl_log("IPv4 ACL entries %u:\n", acl_num_ipv4);
+	dump_ipv4_rules((struct acl4_rule *)acl_base_ipv4, acl_num_ipv4, 1);
+
+	acl_log("IPv6 Route entries %u:\n", route_num_ipv6);
+	dump_ipv6_rules((struct acl6_rule *)route_base_ipv6, route_num_ipv6, 1);
+
+	acl_log("IPv6 ACL entries %u:\n", acl_num_ipv6);
+	dump_ipv6_rules((struct acl6_rule *)acl_base_ipv6, acl_num_ipv6, 1);
+
+	memset(&acl_config, 0, sizeof(acl_config));
+
+	/* Check sockets a context should be created on */
+	if (socket_id >= NB_SOCKETS) {
+		acl_log("Socket %d is out "
+			"of range %d\n",
+			socket_id, NB_SOCKETS);
+		acl_free_routes();
+		return;
+	}
+
+	acl_config.acx_ipv4[socket_id] = app_acl_init(route_base_ipv4,
+		acl_base_ipv4, route_num_ipv4, acl_num_ipv4,
+		0, socket_id);
+
+	acl_config.acx_ipv6[socket_id] = app_acl_init(route_base_ipv6,
+		acl_base_ipv6, route_num_ipv6, acl_num_ipv6,
+		1, socket_id);
+
+#ifdef L3FWDACL_DEBUG
+	acl_config.rule_ipv4 = (struct acl4_rule *)acl_base_ipv4;
+	acl_config.rule_ipv6 = (struct acl6_rule *)acl_base_ipv6;
+#endif
+
+}
+
+/* main processing loop */
+int
+acl_main_loop(__rte_unused void *dummy)
+{
+	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
+	unsigned int lcore_id;
+	uint64_t prev_tsc, diff_tsc, cur_tsc;
+	int i, nb_rx;
+	uint16_t portid;
+	uint8_t queueid;
+	struct lcore_conf *qconf;
+	int socketid;
+	const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1)
+			/ US_PER_S * BURST_TX_DRAIN_US;
+
+	prev_tsc = 0;
+	lcore_id = rte_lcore_id();
+	qconf = &lcore_conf[lcore_id];
+	socketid = rte_lcore_to_socket_id(lcore_id);
+
+	if (qconf->n_rx_queue == 0) {
+		RTE_LOG(INFO, L3FWD, "lcore %u has nothing to do\n", lcore_id);
+		return 0;
+	}
+
+	RTE_LOG(INFO, L3FWD, "entering main loop on lcore %u\n", lcore_id);
+
+	for (i = 0; i < qconf->n_rx_queue; i++) {
+
+		portid = qconf->rx_queue_list[i].port_id;
+		queueid = qconf->rx_queue_list[i].queue_id;
+		RTE_LOG(INFO, L3FWD,
+			" -- lcoreid=%u portid=%u rxqueueid=%hhu\n",
+			lcore_id, portid, queueid);
+	}
+
+	while (!force_quit) {
+
+		cur_tsc = rte_rdtsc();
+
+		/*
+		 * TX burst queue drain
+		 */
+		diff_tsc = cur_tsc - prev_tsc;
+		if (unlikely(diff_tsc > drain_tsc)) {
+
+			for (i = 0; i < qconf->n_tx_port; ++i) {
+				portid = qconf->tx_port_id[i];
+				if (qconf->tx_mbufs[portid].len == 0)
+					continue;
+				send_burst(qconf,
+					qconf->tx_mbufs[portid].len,
+					portid);
+				qconf->tx_mbufs[portid].len = 0;
+			}
+
+			prev_tsc = cur_tsc;
+		}
+
+		/*
+		 * Read packet from RX queues
+		 */
+		for (i = 0; i < qconf->n_rx_queue; ++i) {
+
+			portid = qconf->rx_queue_list[i].port_id;
+			queueid = qconf->rx_queue_list[i].queue_id;
+			nb_rx = rte_eth_rx_burst(portid, queueid,
+				pkts_burst, MAX_PKT_BURST);
+
+			if (nb_rx > 0) {
+				struct acl_search_t acl_search;
+
+				l3fwd_acl_prepare_acl_parameter(pkts_burst, &acl_search,
+					nb_rx);
+
+				if (acl_search.num_ipv4) {
+					rte_acl_classify(
+						acl_config.acx_ipv4[socketid],
+						acl_search.data_ipv4,
+						acl_search.res_ipv4,
+						acl_search.num_ipv4,
+						DEFAULT_MAX_CATEGORIES);
+
+					l3fwd_acl_send_packets(
+						qconf,
+						pkts_burst,
+						acl_search.res_ipv4,
+						nb_rx);
+				}
+
+				if (acl_search.num_ipv6) {
+					rte_acl_classify(
+						acl_config.acx_ipv6[socketid],
+						acl_search.data_ipv6,
+						acl_search.res_ipv6,
+						acl_search.num_ipv6,
+						DEFAULT_MAX_CATEGORIES);
+
+					l3fwd_acl_send_packets(
+						qconf,
+						pkts_burst,
+						acl_search.res_ipv6,
+						nb_rx);
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+/* Not used by L3fwd ACL. */
+void *
+acl_get_ipv4_l3fwd_lookup_struct(__rte_unused const int socketid)
+{
+	return NULL;
+}
+
+void *
+acl_get_ipv6_l3fwd_lookup_struct(__rte_unused const int socketid)
+{
+	return NULL;
+}
diff --git a/examples/l3fwd/l3fwd_acl.h b/examples/l3fwd/l3fwd_acl.h
new file mode 100644
index 0000000000..6f170c9bdf
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl.h
@@ -0,0 +1,51 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+
+#ifndef __L3FWD_ACL_H__
+#define __L3FWD_ACL_H__
+
+#if RTE_LOG_DP_LEVEL >= RTE_LOG_DEBUG
+#define L3FWDACL_DEBUG
+#endif
+
+#define MAX_ACL_RULE_NUM	100000
+#define DEFAULT_MAX_CATEGORIES	1
+#define L3FWD_ACL_IPV4_NAME	"l3fwd-acl-ipv4"
+#define L3FWD_ACL_IPV6_NAME	"l3fwd-acl-ipv6"
+
+#define ACL_DENY_SIGNATURE	0xf0000000
+#define RTE_LOGTYPE_L3FWDACL	RTE_LOGTYPE_USER3
+#define acl_log(format, ...)	RTE_LOG(ERR, L3FWDACL, format, ##__VA_ARGS__)
+#define OFF_ETHHEAD	(sizeof(struct rte_ether_hdr))
+#define OFF_IPV42PROTO (offsetof(struct rte_ipv4_hdr, next_proto_id))
+#define OFF_IPV62PROTO (offsetof(struct rte_ipv6_hdr, proto))
+#define MBUF_IPV4_2PROTO(m)	\
+	rte_pktmbuf_mtod_offset((m), uint8_t *, OFF_ETHHEAD + OFF_IPV42PROTO)
+#define MBUF_IPV6_2PROTO(m)	\
+	rte_pktmbuf_mtod_offset((m), uint8_t *, OFF_ETHHEAD + OFF_IPV62PROTO)
+
+/*
+ * ACL rules should have higher priorities than route ones to ensure ACL rule
+ * always be found when input packets have multi-matches in the database.
+ * A exception case is performance measure, which can define route rules with
+ * higher priority and route rules will always be returned in each lookup.
+ * Reserve range from ACL_RULE_PRIORITY_MAX + 1 to
+ * RTE_ACL_MAX_PRIORITY for route entries in performance measure
+ */
+#define ACL_RULE_PRIORITY_MAX 0x10000000
+
+/*
+ * Forward port info save in ACL lib starts from 1
+ * since ACL assume 0 is invalid.
+ * So, need add 1 when saving and minus 1 when forwarding packets.
+ */
+#define FWD_PORT_SHIFT 1
+
+void
+print_one_ipv4_rule(struct acl4_rule *rule, int extra);
+
+void
+print_one_ipv6_rule(struct acl6_rule *rule, int extra);
+
+#endif /* __L3FWD_ACL_H__ */
diff --git a/examples/l3fwd/l3fwd_acl_scalar.h b/examples/l3fwd/l3fwd_acl_scalar.h
new file mode 100644
index 0000000000..eab3b16ae2
--- /dev/null
+++ b/examples/l3fwd/l3fwd_acl_scalar.h
@@ -0,0 +1,112 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2022 Intel Corporation
+ */
+
+#ifndef __L3FWD_ACL_SCALAR_H__
+#define __L3FWD_ACL_SCALAR_H__
+
+#include "l3fwd.h"
+#include "l3fwd_common.h"
+
+static inline void
+l3fwd_acl_prepare_one_packet(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
+	int index)
+{
+	struct rte_mbuf *pkt = pkts_in[index];
+
+	if (RTE_ETH_IS_IPV4_HDR(pkt->packet_type)) {
+		/* Fill acl structure */
+		acl->data_ipv4[acl->num_ipv4] = MBUF_IPV4_2PROTO(pkt);
+		acl->m_ipv4[(acl->num_ipv4)++] = pkt;
+
+	} else if (RTE_ETH_IS_IPV6_HDR(pkt->packet_type)) {
+		/* Fill acl structure */
+		acl->data_ipv6[acl->num_ipv6] = MBUF_IPV6_2PROTO(pkt);
+		acl->m_ipv6[(acl->num_ipv6)++] = pkt;
+	} else {
+		/* Unknown type, drop the packet */
+		rte_pktmbuf_free(pkt);
+	}
+}
+
+static inline void
+l3fwd_acl_prepare_acl_parameter(struct rte_mbuf **pkts_in, struct acl_search_t *acl,
+	int nb_rx)
+{
+	int i;
+
+	acl->num_ipv4 = 0;
+	acl->num_ipv6 = 0;
+
+	/* Prefetch first packets */
+	for (i = 0; i < PREFETCH_OFFSET && i < nb_rx; i++) {
+		rte_prefetch0(rte_pktmbuf_mtod(
+				pkts_in[i], void *));
+	}
+
+	for (i = 0; i < (nb_rx - PREFETCH_OFFSET); i++) {
+		rte_prefetch0(rte_pktmbuf_mtod(pkts_in[
+				i + PREFETCH_OFFSET], void *));
+		l3fwd_acl_prepare_one_packet(pkts_in, acl, i);
+	}
+
+	/* Process left packets */
+	for (; i < nb_rx; i++)
+		l3fwd_acl_prepare_one_packet(pkts_in, acl, i);
+}
+
+static inline void
+send_packets_single(struct lcore_conf *qconf, struct rte_mbuf *pkts[], uint16_t hops[],
+	uint32_t nb_tx)
+{
+	uint32_t j;
+	struct rte_ether_hdr *eth_hdr;
+
+	for (j = 0; j < nb_tx; j++) {
+		/* Run rfc1812 if packet is ipv4 and checks enabled. */
+		rfc1812_process((struct rte_ipv4_hdr *)(rte_pktmbuf_mtod(
+						pkts[j], struct rte_ether_hdr *) + 1),
+						&hops[j], pkts[j]->packet_type);
+
+		/* Set MAC addresses. */
+		eth_hdr = rte_pktmbuf_mtod(pkts[j], struct rte_ether_hdr *);
+		*(uint64_t *)&eth_hdr->dst_addr = dest_eth_addr[hops[j]];
+		rte_ether_addr_copy(&ports_eth_addr[hops[j]],
+						&eth_hdr->src_addr);
+	}
+
+	for (j  = 0; j != nb_tx; j++) {
+		if (hops[j] != BAD_PORT)
+			send_single_packet(qconf, pkts[j], hops[j]);
+		else
+			rte_pktmbuf_free(pkts[j]);
+	}
+}
+
+static inline void
+l3fwd_acl_send_packets(struct lcore_conf *qconf, struct rte_mbuf *pkts[], uint32_t res[],
+	uint32_t nb_tx)
+{
+	uint32_t i;
+	uint16_t dst_port[nb_tx];
+
+	for (i = 0; i != nb_tx; i++) {
+		if (likely((res[i] & ACL_DENY_SIGNATURE) == 0 && res[i] != 0)) {
+			dst_port[i] = res[i] - FWD_PORT_SHIFT;
+		} else {
+			dst_port[i] = BAD_PORT;
+#ifdef L3FWDACL_DEBUG
+			if ((res & ACL_DENY_SIGNATURE) != 0) {
+				if (RTE_ETH_IS_IPV4_HDR(pkts[i]->packet_type))
+					dump_acl4_rule(pkts[i], res[i]);
+				else if (RTE_ETH_IS_IPV6_HDR(pkt[i]->packet_type))
+					dump_acl6_rule(pkt[i], res[i]);
+			}
+#endif
+		}
+	}
+
+	send_packets_single(qconf, pkts, dst_port, nb_tx);
+}
+
+#endif /* __L3FWD_ACL_SCALAR_H__ */
diff --git a/examples/l3fwd/l3fwd_route.h b/examples/l3fwd/l3fwd_route.h
index adf33dbf46..467c4d2859 100644
--- a/examples/l3fwd/l3fwd_route.h
+++ b/examples/l3fwd/l3fwd_route.h
@@ -5,6 +5,7 @@ 
 /* Log file related character defs. */
 #define COMMENT_LEAD_CHAR	('#')
 #define ROUTE_LEAD_CHAR		('R')
+#define ACL_LEAD_CHAR		('@')
 
 #define	IPV6_ADDR_LEN	16
 #define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
@@ -96,11 +97,26 @@  read_config_files_lpm(void);
 void
 read_config_files_em(void);
 
+void
+read_config_files_acl(void);
+
 void
 em_free_routes(void);
 
 void
 lpm_free_routes(void);
 
+void
+acl_free_routes(void);
+
+void
+l3fwd_set_alg(const char *optarg);
+
+void
+l3fwd_set_rule_ipv6_name(const char *optarg);
+
+void
+l3fwd_set_rule_ipv4_name(const char *optarg);
+
 int
 is_bypass_line(const char *buff);
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index a629198223..38091aac39 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -59,17 +59,17 @@  uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
 /**< Ports set in promiscuous mode off by default. */
 static int promiscuous_on;
 
-/* Select Longest-Prefix, Exact match or Forwarding Information Base. */
+/* Select Longest-Prefix, Exact match, Forwarding Information Base or Access Control. */
 enum L3FWD_LOOKUP_MODE {
 	L3FWD_LOOKUP_DEFAULT,
 	L3FWD_LOOKUP_LPM,
 	L3FWD_LOOKUP_EM,
-	L3FWD_LOOKUP_FIB
+	L3FWD_LOOKUP_FIB,
+	L3FWD_LOOKUP_ACL
 };
 static enum L3FWD_LOOKUP_MODE lookup_mode;
 
 /* Global variables. */
-
 static int numa_on = 1; /**< NUMA is enabled by default. */
 static int parse_ptype; /**< Parse packet type using rx callback, and */
 			/**< disabled by default */
@@ -187,6 +187,17 @@  static struct l3fwd_lkp_mode l3fwd_fib_lkp = {
 	.free_routes			= lpm_free_routes,
 };
 
+static struct l3fwd_lkp_mode l3fwd_acl_lkp = {
+	.read_config_files		= read_config_files_acl,
+	.setup                  = setup_acl,
+	.check_ptype            = em_check_ptype,
+	.cb_parse_ptype         = em_cb_parse_ptype,
+	.main_loop              = acl_main_loop,
+	.get_ipv4_lookup_struct = acl_get_ipv4_l3fwd_lookup_struct,
+	.get_ipv6_lookup_struct = acl_get_ipv6_l3fwd_lookup_struct,
+	.free_routes			= acl_free_routes,
+};
+
 /*
  * 198.18.0.0/16 are set aside for RFC2544 benchmarking (RFC5735).
  * 198.18.{0-15}.0/24 = Port {0-15}
@@ -236,18 +247,24 @@  const struct ipv6_l3fwd_route ipv6_l3fwd_route_array[] = {
 /*
  * API's called during initialization to setup ACL/EM/LPM rules.
  */
-static void
+void
 l3fwd_set_rule_ipv4_name(const char *optarg)
 {
 	parm_config.rule_ipv4_name = optarg;
 }
 
-static void
+void
 l3fwd_set_rule_ipv6_name(const char *optarg)
 {
 	parm_config.rule_ipv6_name = optarg;
 }
 
+void
+l3fwd_set_alg(const char *optarg)
+{
+	parm_config.alg = parse_acl_alg(optarg);
+}
+
 /*
  * Setup lookup methods for forwarding.
  * Currently exact-match, longest-prefix-match and forwarding information
@@ -262,6 +279,9 @@  setup_l3fwd_lookup_tables(void)
 	/* Setup FIB lookup functions. */
 	else if (lookup_mode == L3FWD_LOOKUP_FIB)
 		l3fwd_lkp = l3fwd_fib_lkp;
+	/* Setup ACL lookup functions. */
+	else if (lookup_mode == L3FWD_LOOKUP_ACL)
+		l3fwd_lkp = l3fwd_acl_lkp;
 	/* Setup LPM lookup functions. */
 	else
 		l3fwd_lkp = l3fwd_lpm_lkp;
@@ -361,6 +381,9 @@  init_lcore_rx_queues(void)
 static void
 print_usage(const char *prgname)
 {
+	char alg[PATH_MAX];
+
+	usage_acl_alg(alg, sizeof(alg));
 	fprintf(stderr, "%s [EAL options] --"
 		" -p PORTMASK"
 		"  --rule_ipv4=FILE"
@@ -387,7 +410,8 @@  print_usage(const char *prgname)
 		"  -P : Enable promiscuous mode\n"
 		"  --lookup: Select the lookup method\n"
 		"            Default: lpm\n"
-		"            Accepted: em (Exact Match), lpm (Longest Prefix Match), fib (Forwarding Information Base)\n"
+		"            Accepted: em (Exact Match), lpm (Longest Prefix Match), fib (Forwarding Information Base),\n"
+		"                      acl (Access Control List)\n"
 		"  --config (port,queue,lcore): Rx queue configuration\n"
 		"  --rx-queue-size NPKTS: Rx queue size in decimal\n"
 		"            Default: %d\n"
@@ -416,8 +440,13 @@  print_usage(const char *prgname)
 		"  -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, RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT);
+		"                    2 kinds of rules are supported.\n"
+		"                    One is ACL entry at while line leads with character '%c',\n"
+		"                    another is route entry at while line leads with character '%c'.\n"
+		"  --rule_ipv6=FILE: Specify the ipv6 rules entries file.\n"
+		"  --alg: ACL classify method to use, one of: %s.\n\n",
+		prgname, RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT,
+		ACL_LEAD_CHAR, ROUTE_LEAD_CHAR, alg);
 }
 
 static int
@@ -632,8 +661,10 @@  parse_lookup(const char *optarg)
 		lookup_mode = L3FWD_LOOKUP_LPM;
 	else if (!strcmp(optarg, "fib"))
 		lookup_mode = L3FWD_LOOKUP_FIB;
+	else if (!strcmp(optarg, "acl"))
+		lookup_mode = L3FWD_LOOKUP_ACL;
 	else {
-		fprintf(stderr, "Invalid lookup option! Accepted options: em, lpm, fib\n");
+		fprintf(stderr, "Invalid lookup option! Accepted options: acl, em, lpm, fib\n");
 		return -1;
 	}
 	return 0;
@@ -667,6 +698,7 @@  static const char short_options[] =
 #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"
+#define CMD_LINE_OPT_ALG "alg"
 
 enum {
 	/* long options mapped to a short option */
@@ -685,6 +717,7 @@  enum {
 	CMD_LINE_OPT_PARSE_PTYPE_NUM,
 	CMD_LINE_OPT_RULE_IPV4_NUM,
 	CMD_LINE_OPT_RULE_IPV6_NUM,
+	CMD_LINE_OPT_ALG_NUM,
 	CMD_LINE_OPT_PARSE_PER_PORT_POOL,
 	CMD_LINE_OPT_MODE_NUM,
 	CMD_LINE_OPT_EVENTQ_SYNC_NUM,
@@ -716,6 +749,7 @@  static const struct option lgopts[] = {
 	{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},
+	{CMD_LINE_OPT_ALG,   1, 0, CMD_LINE_OPT_ALG_NUM},
 	{NULL, 0, 0, 0}
 };
 
@@ -884,6 +918,9 @@  parse_args(int argc, char **argv)
 		case CMD_LINE_OPT_RULE_IPV6_NUM:
 			l3fwd_set_rule_ipv6_name(optarg);
 			break;
+		case CMD_LINE_OPT_ALG_NUM:
+			l3fwd_set_alg(optarg);
+			break;
 		default:
 			print_usage(prgname);
 			return -1;
@@ -923,7 +960,7 @@  parse_args(int argc, char **argv)
 	 * as default match.
 	 */
 	if (lookup_mode == L3FWD_LOOKUP_DEFAULT) {
-		fprintf(stderr, "Neither LPM, EM, or FIB selected, defaulting to LPM\n");
+		fprintf(stderr, "Neither ACL, LPM, EM, or FIB selected, defaulting to LPM\n");
 		lookup_mode = L3FWD_LOOKUP_LPM;
 	}
 
@@ -937,6 +974,12 @@  parse_args(int argc, char **argv)
 		hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT;
 	}
 
+	/* For ACL, update port config rss hash filter */
+	if (lookup_mode == L3FWD_LOOKUP_ACL) {
+		port_conf.rx_adv_conf.rss_conf.rss_hf |=
+				RTE_ETH_RSS_UDP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_SCTP;
+	}
+
 	if (optind >= 0)
 		argv[optind-1] = prgname;
 
@@ -992,7 +1035,7 @@  init_mem(uint16_t portid, unsigned int nb_mbuf)
 				printf("Allocated mbuf pool on socket %d\n",
 					socketid);
 
-			/* Setup LPM, EM(f.e Hash) or FIB. But, only once per
+			/* Setup ACL, LPM, EM(f.e Hash) or FIB. But, only once per
 			 * available socket.
 			 */
 			if (!lkp_per_socket[socketid]) {
diff --git a/examples/l3fwd/meson.build b/examples/l3fwd/meson.build
index 0830b3eb31..2fa00e5b74 100644
--- a/examples/l3fwd/meson.build
+++ b/examples/l3fwd/meson.build
@@ -7,8 +7,9 @@ 
 # DPDK instance, use 'make'
 
 allow_experimental_apis = true
-deps += ['hash', 'lpm', 'fib', 'eventdev']
+deps += ['hash', 'lpm', 'fib', 'eventdev', 'acl']
 sources = files(
+        'l3fwd_acl.c',
         'l3fwd_em.c',
         'l3fwd_event.c',
         'l3fwd_event_internal_port.c',
diff --git a/examples/meson.build b/examples/meson.build
index 268422a257..78de0e1f37 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -32,7 +32,6 @@  all_examples = [
         'l2fwd-jobstats',
         'l2fwd-keepalive',
         'l3fwd',
-        'l3fwd-acl',
         'l3fwd-graph',
         'l3fwd-power',
         'link_status_interrupt',