diff --git a/MAINTAINERS b/MAINTAINERS
index b90aeea..1785b02 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -261,6 +261,8 @@ Chelsio cxgbe
 M: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
 F: drivers/net/cxgbe/
 F: doc/guides/nics/cxgbe.rst
+F: examples/test-cxgbe-filters/
+F: doc/guides/sample_app_ug/test_cxgbe_filters.rst
 
 Cisco enic
 M: John Daley <johndale@cisco.com>
diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst
index 8a646dd..fdc0340 100644
--- a/doc/guides/sample_app_ug/index.rst
+++ b/doc/guides/sample_app_ug/index.rst
@@ -73,6 +73,7 @@ Sample Applications User Guide
     proc_info
     ptpclient
     performance_thread
+    test_cxgbe_filters
 
 **Figures**
 
diff --git a/doc/guides/sample_app_ug/test_cxgbe_filters.rst b/doc/guides/sample_app_ug/test_cxgbe_filters.rst
new file mode 100644
index 0000000..9012a58
--- /dev/null
+++ b/doc/guides/sample_app_ug/test_cxgbe_filters.rst
@@ -0,0 +1,694 @@
+..  BSD LICENSE
+    Copyright 2015-2016 Chelsio Communications.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+    * Neither the name of Chelsio Communications nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Test CXGBE Filters Application
+==============================
+
+The test cxgbe filters application provides a command line interface to
+test Chelsio NIC packet classification and filtering features available
+in hardware.
+
+Overview
+--------
+
+Chelsio T5 NICs support packet classification and filtering in hardware.
+This feature can be used in the ingress path to:
+
+- Steer ingress packets that meet ACL (Access Control List) accept criteria
+  to a particular receive queue.
+
+- Switch (proxy) ingress packets that meet ACL accept criteria to an output
+  port, with optional header rewrite.
+
+- Drop ingress packets that fail ACL accept criteria.
+
+There are two types of filters that can be set, namely LE-TCAM (Maskfull)
+filters and HASH (Maskless) filters.  LE-TCAM filters allow specifying masks
+to the accept criteria to allow specifying a match for a range of values;
+whereas, HASH filters ignore masks and hence enforce a more strict accept
+criteria.
+
+The fields that can be specified for the accept criteria are based on the
+filter selection combination set in the firmware configuration (t5-config.txt)
+file flashed. Please see *CXGBE Poll Mode Driver NIC Guide* for instructions
+on how to flash the firmware configuration file onto Chelsio NICs.
+
+By default, the selection combination automatically includes source/
+destination IPV4/IPV6 address, and source/destination layer 4 port
+addresses.  In addition to the above, more combinations can be added by
+modifying the t5-config.txt firmware configuration file.
+
+For example, consider the following combination that has been set in
+t5-config.txt:
+
+.. code-block:: console
+
+   filterMode = ethertype, protocol, tos, vlan, port
+   filterMask = ethertype, protocol, tos, vlan, port
+
+In the above example, in addition to source/destination IPV4/IPV6
+addresses and layer 4 source/destination port addresses, a packet can also
+be matched against ethertype field set in the ethernet header, IP protocol
+and tos field set in the IP header, inner VLAN tag, and physical ingress
+port number, respectively.
+
+You can create 496 LE-TCAM filters and ~0.5 million HASH filter rules.
+For more information, please visit `Chelsio Communications Official Website
+<http://www.chelsio.com>`_.
+
+Compiling the Application
+-------------------------
+
+To compile the application:
+
+#. Turn on command line library in the corresponding config/common_*
+   configuration file. For example, for x86_64-native-linuxapp-gcc target,
+   enable ``CONFIG_RTE_LIBRTE_CMDLINE`` in config/common_linuxapp as follows:
+
+   .. code-block:: console
+
+      CONFIG_RTE_LIBRTE_CMDLINE=y
+
+#. Go to the sample application directory:
+
+   .. code-block:: console
+
+      export RTE_SDK=/path/to/rte_sdk
+      cd ${RTE_SDK}/examples/test-cxgbe-filters
+
+#. Set the target (a default target is used if not specified). For example:
+
+   .. code-block:: console
+
+      export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+   See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
+
+#. Build the application as follows:
+
+   .. code-block:: console
+
+      make
+
+Running the Application
+-----------------------
+
+Ensure, that the Chelsio NICs are bound to DPDK, and run the application
+from the build directory as follows:
+
+.. code-block:: console
+
+   ./build/test_cxgbe_filters
+
+When successful, a command line prompt appears as shown below:
+
+.. code-block:: console
+
+   [...]
+   PMD: rte_cxgbe_pmd:  0000:04:00.4 Chelsio rev 0 1000/10GBASE-SFP
+   PMD: rte_cxgbe_pmd:  0000:04:00.4 Chelsio rev 0 1000/10GBASE-SFP
+   USER1: Initializing NIC port 0 ...
+   Port 0: 00:07:43:2F:08:60
+   USER1: Initializing NIC port 1 ...
+   Port 1: 00:07:43:2F:08:68
+   PMD: rte_cxgbe_pmd: Port0: passive DA port module inserted
+   PMD: rte_cxgbe_pmd: Port1: passive DA port module inserted
+   USER1: Port 0 (10 Gbps) UP
+   USER1: Port 1 (10 Gbps) UP
+   USER1: Core 1 is doing RX for port 0
+   USER1: Core 2 is doing RX for port 1
+   cxgbe>
+
+There are a number of commands available.  Run **help** to get the command list.
+
+.. code-block:: console
+
+   cxgbe> help
+
+   Help:
+   -----
+
+   show (stats|port|fdir) (port_id)
+       Display information for port_id.
+
+   show_all (stats|port)
+       Display information for all ports.
+
+   clear stats (port_id)
+       Clear information for port_id.
+
+   clear_all stats
+       Clear information for all ports.
+
+   quit
+       Quit to prompt.
+
+   filter (port_id) (add|del) (ipv4|ipv6)
+    mode (maskfull|maskless) (no-prio|prio)
+    ingress-port (iport) (iport_mask)
+    ether (ether_type) (ether_type_mask)
+    vlan (inner_vlan) (inner_vlan_mask) (outer_vlan) (outer_vlan_mask)
+    ip (tos) (tos_mask) (proto) (proto_mask)
+    (src_ip_address) (src_ip_mask) (dst_ip_address) (dst_ip_mask)
+    (src_port) (src_port_mask) (dst_port) (dst_port_mask)
+    (drop|fwd|switch) queue (queue_id)
+    (port-none|port-redirect) (egress_port)
+    (ether-none|mac-rewrite|mac-swap) (src_mac) (dst_mac)
+    (vlan-none|vlan-rewrite|vlan-delete) (new_vlan)
+    (nat-none|nat-rewrite) (nat_src_ip) (nat_dst_ip)
+    (nat_src_port) (nat_dst_port)
+    fd_id (fd_id_value)
+       Add/Del a cxgbe flow director filter.
+
+Add/Delete Filters
+------------------
+
+The command line to add/delete filters is given below. Note that the
+command is too long to fit on one line and hence is shown wrapped
+at "\\" for display purposes.  In real prompt, these commands should
+be on a single line without the "\\".
+
+  .. code-block:: console
+
+     cxgbe> filter (port_id) (add|del) (ipv4|ipv6) \
+            mode (maskfull|maskless) (no-prio|prio) \
+            ingress-port (iport) (iport_mask) \
+            ether (ether_type) (ether_type_mask) \
+            vlan (inner_vlan) (inner_vlan_mask) \
+            (outer_vlan) (outer_vlan_mask) \
+            ip (tos) (tos_mask) (proto) (proto_mask) \
+            (src_ip_address) (src_ip_mask) \
+            (dst_ip_address) (dst_ip_mask) \
+            (src_port) (src_port_mask) (dst_port) (dst_port_mask) \
+            (drop|fwd|switch) queue (queue_id) \
+            (port-none|port-redirect) (egress_port) \
+            (ether-none|mac-rewrite|mac-swap) (src_mac) (dst_mac) \
+            (vlan-none|vlan-rewrite|vlan-delete) (new_vlan) \
+            (nat-none|nat-rewrite) (nat_src_ip) (nat_dst_ip) \
+            (nat_src_port) (nat_dst_port) \
+            fd_id (fd_id_value)
+
+LE-TCAM (Maskfull) Filters
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For maskfull filters, if the match field and its corresponding mask in
+the accept criteria are both set to 0, then they are not considered for
+accept criteria against the packet.
+
+The **fd_id** value specified for maskfull filters have a priority with
+value 0 having the highest priority.  If a packet matches a filter rule
+with lower **fd_id**, then its action is executed immediately and the
+remaining filter rules are ignored.
+
+Maskfull IPv6 filters occupy 4 **fd_id** slots and hence must be on 4 slot
+boundary.  IPv4 filters on the other hand occupy only 1 slot.  Thus, if
+a slot is being occupied by an IPv6 filter rule, then an IPv4 filter rule
+can not be set on the occupied slot.
+
+By default, maskless filter rules have higher priority over the maskfull
+filter rules.  Thus, if a packet could match both a maskfull and a maskless
+filter rule, then **prio** value can be specified to allow maskfull filter
+rule to have a higher priority over the maskless filter rule.
+
+DROP Filter Example
+^^^^^^^^^^^^^^^^^^^
+
+An example to set a drop maskfull filter is given below:
+
+#. Generate some traffic destined for 102.1.2.0/24 network.  The app's rxq
+   should have successfully received the traffic as shown below:
+
+   .. code-block:: console
+
+      cxgbe> show stats 0
+      ################################################################
+      App Stats for port: 0
+      # of Received Packets: 1000000
+
+      Port Extended Stats
+      rx_good_packets: 1000000
+      tx_good_packets: 0
+      rx_good_bytes: 64000000
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 1000000
+      rx_q0_bytes: 64000000
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+
+#. To drop all traffic coming for 102.1.2.0/24 network, add a maskfull
+   filter as follows:
+
+   .. code-block:: console
+
+      cxgbe> filter 0 add ipv4 mode maskfull \
+             no-prio ingress-port 0 0 ether 0 0 vlan 0 0 0 0 \
+             ip 0 0 0 0 0.0.0.0 0.0.0.0 102.1.2.0 255.255.255.0 0 0 0 0 \
+             drop queue 0 port-none 0 \
+             ether-none 00:00:00:00:00:00 00:00:00:00:00:00 \
+             vlan-none 0 nat-none 0.0.0.0 0.0.0.0 0 0 \
+             fd_id 0
+
+#. Generate the same traffic destined for 102.1.2.0/24 network again.  The
+   traffic would have been arrived at the port, but the app's rxq should not
+   have received the traffic; i.e. the traffic had been successfully dropped
+   by the hardware, as shown below:
+
+   .. code-block:: console
+
+      cxgbe> show stats 0
+      ################################################################
+      App Stats for port: 0
+      # of Received Packets: 1000000
+
+      Port Extended Stats
+      rx_good_packets: 2000000
+      tx_good_packets: 0
+      rx_good_bytes: 128000000
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 1000000
+      rx_q0_bytes: 64000000
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+
+#. Delete the maskfull filter as follows:
+
+   .. code-block:: console
+
+      cxgbe> filter 0 del ipv4 mode maskfull \
+             no-prio ingress-port 0 0 ether 0 0 vlan 0 0 0 0 \
+             ip 0 0 0 0 0.0.0.0 0.0.0.0 0.0.0.0 0.0.0.0 0 0 0 0 \
+             drop queue 0 port-none 0 \
+             ether-none 00:00:00:00:00:00 00:00:00:00:00:00 \
+             vlan-none 0 nat-none 0.0.0.0 0.0.0.0 0 0 \
+             fd_id 0
+
+#. Generate the same traffic destined for 102.1.2.0/24 network again.
+   The app's rxq should have successfully received the traffic again
+   as shown below:
+
+   .. code-block:: console
+
+      cxgbe> show stats 0
+      ################################################################
+      App Stats for port: 0
+      # of Received Packets: 2000000
+
+      Port Extended Stats
+      rx_good_packets: 3000000
+      tx_good_packets: 0
+      rx_good_bytes: 192000000
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 2000000
+      rx_q0_bytes: 128000000
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+
+STEER Filter Example
+^^^^^^^^^^^^^^^^^^^^
+
+An example to set a steering maskfull filter to steer traffic from port
+0 to port 1's rxq is given below:
+
+#. Generate some traffic destined for 102.1.2.0/24 network to port 0.
+   The port 0's rxq should have successfully received the traffic,
+   as shown below:
+
+   .. code-block:: console
+
+      cxgbe> show_all stats
+      ################################################################
+      App Stats for port: 0
+      # of Received Packets: 1000000
+
+      Port Extended Stats
+      rx_good_packets: 1000000
+      tx_good_packets: 0
+      rx_good_bytes: 64000000
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 1000000
+      rx_q0_bytes: 64000000
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+      ################################################################
+      App Stats for port: 1
+      # of Received Packets: 0
+
+      Port Extended Stats
+      rx_good_packets: 0
+      tx_good_packets: 0
+      rx_good_bytes: 0
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 0
+      rx_q0_bytes: 0
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+
+#. To steer all traffic coming for 102.1.2.0/24 network from port 0 to
+   port 1's rxq, add a maskfull filter as follows:
+
+   .. code-block:: console
+
+      cxgbe> filter 1 add ipv4 mode maskfull \
+             no-prio ingress-port 0 0 ether 0 0 vlan 0 0 0 0 \
+             ip 0 0 0 0 0.0.0.0 0.0.0.0 102.1.2.0 255.255.255.0 0 0 0 0 \
+             fwd queue 0 port-none 0 \
+             ether-none 00:00:00:00:00:00 00:00:00:00:00:00 \
+             vlan-none 0 nat-none 0.0.0.0 0.0.0.0 0 0 \
+             fd_id 0
+
+#. Generate the same traffic destined for 102.1.2.0/24 network to port 0
+   again.  The traffic would have arrived at port 0, but it should be
+   steered to port 1's rxq, as shown below:
+
+   .. code-block:: console
+
+      cxgbe> show_all stats
+      ################################################################
+      App Stats for port: 0
+      # of Received Packets: 1000000
+
+      Port Extended Stats
+      rx_good_packets: 2000000
+      tx_good_packets: 0
+      rx_good_bytes: 128000000
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 1000000
+      rx_q0_bytes: 64000000
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+      ################################################################
+      App Stats for port: 1
+      # of Received Packets: 1000000
+
+      Port Extended Stats
+      rx_good_packets: 0
+      tx_good_packets: 0
+      rx_good_bytes: 0
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 1000000
+      rx_q0_bytes: 64000000
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+
+HASH (Maskless) Filters
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Maskless filters perform match based on the hash generated from the
+fields selected in the accept criteria.  Maskless filters ignore masks
+if specified.  Both IPv4 and IPv6 filter rules occupy only 1 **fd_id**
+slot for maskless filters.
+
+Maskless filters require a special firmware configuration file. Please
+see *CXGBE Poll Mode Driver NIC Guide* for instructions on how to flash
+the firmware configuration file onto Chelsio NICs.
+
+SWITCH Filter Example
+^^^^^^^^^^^^^^^^^^^^^
+
+An example to set a switch maskless filter with source and destination
+mac addresses swapped is given below:
+
+#. Generate some TCP traffic destined for 102.1.2.2 with destination port
+   12865 coming from 102.1.2.1 with source port 12000.  The app's port 0
+   rxq should have successfully received the traffic as shown below:
+
+   .. code-block:: console
+
+      cxgbe> show_all stats
+      ################################################################
+      App Stats for port: 0
+      # of Received Packets: 1000000
+
+      Port Extended Stats
+      rx_good_packets: 1000000
+      tx_good_packets: 0
+      rx_good_bytes: 64000000
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 1000000
+      rx_q0_bytes: 64000000
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+      ################################################################
+      App Stats for port: 1
+      # of Received Packets: 0
+
+      Port Extended Stats
+      rx_good_packets: 0
+      tx_good_packets: 0
+      rx_good_bytes: 0
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 0
+      rx_q0_bytes: 0
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+
+#. To switch the traffic from port 0 to port 1 and swap the source and
+   destination mac addresses, add a maskless filter as follows:
+
+   .. code-block:: console
+
+      cxgbe> filter 0 add ipv4 mode maskless \
+             no-prio ingress-port 0 0 ether 0 0 vlan 0 0 0 0 \
+             ip 0 0 0 0 102.1.2.1 0.0.0.0 102.1.2.2 0.0.0.0 12000 0 12865 0 \
+             switch queue 0 port-redirect 1 \
+             mac-swap 00:00:00:00:00:00 00:00:00:00:00:00 \
+             vlan-none 0 nat-none 0.0.0.0 0.0.0.0 0 0 \
+             fd_id 0
+
+#. Generate the same traffic again and the traffic from port 0 gets
+   switched to port 1 without reaching the app's rx queues as shown below.
+   Also, the source and destination mac addresses should have been swapped
+   if the traffic had been captured.
+
+   .. code-block:: console
+
+      cxgbe> show_all stats
+      ################################################################
+      App Stats for port: 0
+      # of Received Packets: 1000000
+
+      Port Extended Stats
+      rx_good_packets: 2000000
+      tx_good_packets: 0
+      rx_good_bytes: 128000000
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 1000000
+      rx_q0_bytes: 64000000
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+      ################################################################
+      App Stats for port: 1
+      # of Received Packets: 0
+
+      Port Extended Stats
+      rx_good_packets: 0
+      tx_good_packets: 1000000
+      rx_good_bytes: 0
+      tx_good_bytes: 64000000
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 0
+      rx_q0_bytes: 0
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+
+#. To delete the maskless filter, run:
+
+   .. code-block:: console
+
+      cxgbe> filter 0 del ipv4 mode maskless \
+             no-prio ingress-port 0 0 ether 0 0 vlan 0 0 0 0 \
+             ip 0 0 0 0 0.0.0.0 0.0.0.0 0.0.0.0 0.0.0.0 0 0 0 0 \
+             fwd queue 0 port-none 0 \
+             ether-none 00:00:00:00:00:00 00:00:00:00:00:00 \
+             vlan-none 0 nat-none 0.0.0.0 0.0.0.0 0 0 \
+             fd_id 0
+
+NAT Offload Filter Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+An example to perform NAT with a switch maskless filter with destination
+ip and port addresses re-written is given below:
+
+#. Generate some TCP traffic destined for 102.1.2.2 with destination port
+   12865 coming from 102.1.2.1 with source port 12000.  The app's port 0
+   rxq should have successfully received the traffic as shown below:
+
+   .. code-block:: console
+
+      cxgbe> show_all stats
+      ################################################################
+      App Stats for port: 0
+      # of Received Packets: 1000000
+
+      Port Extended Stats
+      rx_good_packets: 1000000
+      tx_good_packets: 0
+      rx_good_bytes: 64000000
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 1000000
+      rx_q0_bytes: 64000000
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+      ################################################################
+      App Stats for port: 1
+      # of Received Packets: 0
+
+      Port Extended Stats
+      rx_good_packets: 0
+      tx_good_packets: 0
+      rx_good_bytes: 0
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 0
+      rx_q0_bytes: 0
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+
+#. To perform NAT and switch the traffic from port 0 to port 1 with
+   destination ip and port addresses re-written to 10.1.1.1 and 14000
+   respectively, add a maskless filter as follows:
+
+   .. code-block:: console
+
+      cxgbe> filter 0 add ipv4 mode maskless \
+             no-prio ingress-port 0 0 ether 0 0 vlan 0 0 0 0 \
+             ip 0 0 0 0 102.1.2.1 0.0.0.0 102.1.2.2 0.0.0.0 12000 0 12865 0 \
+             switch queue 0 port-redirect 1 \
+             ether-none 00:00:00:00:00:00 00:00:00:00:00:00 \
+             vlan-none 0 nat-rewrite 102.1.2.1 10.1.1.1 12000 14000 \
+             fd_id 0
+
+#. Generate the same traffic again and the traffic from port 0 gets
+   switched to port 1 without reaching the app's rx queues as shown below.
+   Also, the destination ip and port addresses should have been re-written
+   if the traffic had been captured.
+
+   .. code-block:: console
+
+      cxgbe> show_all stats
+      ################################################################
+      App Stats for port: 0
+      # of Received Packets: 1000000
+
+      Port Extended Stats
+      rx_good_packets: 2000000
+      tx_good_packets: 0
+      rx_good_bytes: 128000000
+      tx_good_bytes: 0
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 1000000
+      rx_q0_bytes: 64000000
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
+      ################################################################
+      App Stats for port: 1
+      # of Received Packets: 0
+
+      Port Extended Stats
+      rx_good_packets: 0
+      tx_good_packets: 1000000
+      rx_good_bytes: 0
+      tx_good_bytes: 64000000
+      rx_errors: 0
+      tx_errors: 0
+      rx_mbuf_allocation_errors: 0
+      rx_q0_packets: 0
+      rx_q0_bytes: 0
+      rx_q0_errors: 0
+      tx_q0_packets: 0
+      tx_q0_bytes: 0
+      ################################################################
diff --git a/examples/Makefile b/examples/Makefile
index 1cb4785..ed7ad49 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -71,6 +71,7 @@ DIRS-y += quota_watermark
 DIRS-$(CONFIG_RTE_ETHDEV_RXTX_CALLBACKS) += rxtx_callbacks
 DIRS-y += skeleton
 DIRS-$(CONFIG_RTE_LIBRTE_VHOST) += tep_termination
+DIRS-$(CONFIG_RTE_LIBRTE_CMDLINE) += test-cxgbe-filters
 DIRS-$(CONFIG_RTE_LIBRTE_TIMER) += timer
 DIRS-$(CONFIG_RTE_LIBRTE_VHOST) += vhost
 DIRS-$(CONFIG_RTE_LIBRTE_XEN_DOM0) += vhost_xen
diff --git a/examples/test-cxgbe-filters/Makefile b/examples/test-cxgbe-filters/Makefile
new file mode 100644
index 0000000..9e9883b
--- /dev/null
+++ b/examples/test-cxgbe-filters/Makefile
@@ -0,0 +1,63 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015-2016 Chelsio Communications.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Chelsio Communications nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifeq ($(CONFIG_RTE_LIBRTE_CMDLINE), y)
+
+# binary name
+APP = test_cxgbe_filters
+
+VPATH += $(SRCDIR)/cxgbe
+
+INC += $(wildcard *.h) $(wildcard cxgbe/*.h)
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+SRCS-y += config.c
+SRCS-y += init.c
+SRCS-y += runtime.c
+SRCS-y += commands.c
+SRCS-y += cxgbe_commands.c
+
+CFLAGS += -I$(SRCDIR) -I$(SRCDIR)/cxgbe
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extapp.mk
+endif
diff --git a/examples/test-cxgbe-filters/commands.c b/examples/test-cxgbe-filters/commands.c
new file mode 100644
index 0000000..09dee94
--- /dev/null
+++ b/examples/test-cxgbe-filters/commands.c
@@ -0,0 +1,429 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015-2016 Chelsio Communications.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Chelsio Communications nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline.h>
+
+#include <rte_common.h>
+#include <rte_ethdev.h>
+#include <rte_malloc.h>
+
+#include "main.h"
+#include "commands.h"
+
+static int
+port_id_is_invalid(uint8_t port_id)
+{
+	if (port_id < app.n_ports)
+		return 0;
+
+	printf("Invalid port %d. Must be < %d\n", port_id, app.n_ports);
+	return 1;
+}
+
+/****************/
+
+struct cmd_help_result {
+	cmdline_fixed_string_t help;
+};
+
+static void
+cmd_help_parsed(__rte_unused void *parsed_result,
+		struct cmdline *cl,
+		__rte_unused void *data)
+{
+	cmdline_printf(cl,
+		       "\n"
+		       "Help:\n"
+		       "-----\n\n"
+
+		       "show (stats|port|fdir) (port_id)\n"
+		       "    Display information for port_id.\n\n"
+
+		       "show_all (stats|port)\n"
+		       "    Display information for all ports.\n\n"
+
+		       "clear stats (port_id)\n"
+		       "    Clear information for port_id.\n\n"
+
+		       "clear_all stats\n"
+		       "    Clear information for all ports.\n\n"
+
+		       "quit\n"
+		       "    Quit to prompt.\n\n"
+
+		       "filter (port_id) (add|del) (ipv4|ipv6)"
+		       " mode (maskfull|maskless) (no-prio|prio)"
+		       " ingress-port (iport) (iport_mask)"
+		       " ether (ether_type) (ether_type_mask)"
+		       " vlan (inner_vlan) (inner_vlan_mask)"
+		       " (outer_vlan) (outer_vlan_mask)"
+		       " ip (tos) (tos_mask) (proto) (proto_mask)"
+		       " (src_ip_address) (src_ip_mask)"
+		       " (dst_ip_address) (dst_ip_mask)"
+		       " (src_port) (src_port_mask) (dst_port) (dst_port_mask)"
+		       " (drop|fwd|switch) queue (queue_id)"
+		       " (port-none|port-redirect) (egress_port)"
+		       " (ether-none|mac-rewrite|mac-swap) (src_mac) (dst_mac)"
+		       " (vlan-none|vlan-rewrite|vlan-delete) (new_vlan)"
+		       " (nat-none|nat-rewrite) (nat_src_ip) (nat_dst_ip)"
+		       " (nat_src_port) (nat_dst_port)"
+		       " fd_id (fd_id_value)\n"
+		       "    Add/Del a cxgbe flow director filter.\n\n"
+		      );
+}
+
+cmdline_parse_token_string_t cmd_help_help =
+	TOKEN_STRING_INITIALIZER(struct cmd_help_result, help,
+				 "help");
+
+cmdline_parse_inst_t cmd_help = {
+	.f = cmd_help_parsed,
+	.data = NULL,
+	.help_str = "print help",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_help_help,
+		NULL,
+	},
+};
+
+/****************/
+
+struct cmd_show_result {
+	cmdline_fixed_string_t show;
+	cmdline_fixed_string_t op;
+	uint8_t port_id;
+};
+
+static void
+print_xstats(uint8_t port_id)
+{
+	struct rte_eth_xstats *xstats;
+	int len, ret, i;
+
+	if (port_id_is_invalid(port_id))
+		return;
+
+	printf("App Stats for port: %d\n", port_id);
+	printf("# of Received Packets: %" PRIu64 "\n",
+	       app.mbuf_rx[port_id].pkts);
+
+	printf("\nPort Extended Stats\n");
+	len = rte_eth_xstats_get(port_id, NULL, 0);
+	if (len < 0) {
+		printf("Cannot get extended stats\n");
+		return;
+	}
+
+	xstats = malloc(sizeof(xstats[0]) * len);
+	if (!xstats) {
+		printf("Cannot allocate memory for xstats\n");
+		return;
+	}
+
+	ret = rte_eth_xstats_get(port_id, xstats, len);
+	if (ret < 0 || ret > len) {
+		printf("Cannot get xstats\n");
+		free(xstats);
+		return;
+	}
+
+	for (i = 0; i < len; i++)
+		printf("%s: %" PRIu64 "\n", xstats[i].name, xstats[i].value);
+
+	free(xstats);
+}
+
+static void
+print_port(uint8_t port_id)
+{
+	struct ether_addr mac_addr;
+	struct rte_eth_link link;
+	char buf[ETHER_ADDR_FMT_SIZE];
+
+	if (port_id_is_invalid(port_id))
+		return;
+
+	rte_eth_link_get_nowait(port_id, &link);
+	printf("\nInfos for port %d\n", port_id);
+
+	rte_eth_macaddr_get(port_id, &mac_addr);
+	ether_format_addr(buf, ETHER_ADDR_FMT_SIZE, &mac_addr);
+	printf("MAC address: %s", buf);
+
+	printf("\nLink status: %s\n", (link.link_status) ? ("up") : ("down"));
+	printf("Link speed: %u Mbps\n", (unsigned)link.link_speed);
+	printf("Link duplex: %s\n", (link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
+				    ("full-duplex") : ("half-duplex"));
+	printf("Promiscuous mode: %s\n",
+	       rte_eth_promiscuous_get(port_id) ? "enabled" : "disabled");
+	printf("Allmulticast mode: %s\n",
+	       rte_eth_allmulticast_get(port_id) ? "enabled" : "disabled");
+}
+
+static void
+print_fdir(uint8_t port_id)
+{
+	struct rte_eth_fdir_stats fdir_stat;
+	int ret;
+
+	if (port_id_is_invalid(port_id))
+		return;
+
+	ret = rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_FDIR);
+	if (ret < 0) {
+		printf("FDIR is not supported on port %d\n", port_id);
+		return;
+	}
+
+	memset(&fdir_stat, 0, sizeof(fdir_stat));
+	rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR,
+				RTE_ETH_FILTER_STATS, &fdir_stat);
+	printf("FDIR stats for port %d\n", port_id);
+	printf("  free:          %" PRIu32 "\n"
+	       "  add:	         %-10" PRIu64 "  remove:        %" PRIu64 "\n"
+	       "  f_add:         %-10" PRIu64 "  f_remove:      %" PRIu64 "\n",
+	       fdir_stat.free,
+	       fdir_stat.add, fdir_stat.remove,
+	       fdir_stat.f_add, fdir_stat.f_remove);
+}
+
+static void
+cmd_show_parsed(void *parsed_result,
+		__rte_unused struct cmdline *cl,
+		__rte_unused void *data)
+{
+	struct cmd_show_result *res = parsed_result;
+	const char *border = "#######################################";
+
+	printf("%s%s\n", border, border);
+	if (!strcmp(res->op, "stats"))
+		print_xstats(res->port_id);
+	else if (!strcmp(res->op, "port"))
+		print_port(res->port_id);
+	else if (!strcmp(res->op, "fdir"))
+		print_fdir(res->port_id);
+	printf("%s%s\n", border, border);
+}
+
+cmdline_parse_token_string_t cmd_show_show =
+	TOKEN_STRING_INITIALIZER(struct cmd_show_result, show, "show");
+cmdline_parse_token_string_t cmd_show_op =
+	TOKEN_STRING_INITIALIZER(struct cmd_show_result, op, "stats#port#fdir");
+cmdline_parse_token_num_t cmd_show_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_show_result, port_id, UINT8);
+
+cmdline_parse_inst_t cmd_show = {
+	.f = cmd_show_parsed,
+	.data = NULL,
+	.help_str = "show information",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_show_show,
+		(void *)&cmd_show_op,
+		(void *)&cmd_show_port_id,
+		NULL,
+	},
+};
+
+/****************/
+
+struct cmd_show_all_result {
+	cmdline_fixed_string_t show_all;
+	cmdline_fixed_string_t op;
+};
+
+static void
+cmd_show_all_parsed(void *parsed_result,
+		    __rte_unused struct cmdline *cl,
+		    __rte_unused void *data)
+{
+	struct cmd_show_all_result *res = parsed_result;
+	const char *border = "#######################################";
+	unsigned int i;
+
+	for (i = 0; i < app.n_ports; i++) {
+		printf("%s%s\n", border, border);
+		if (!strcmp(res->op, "stats"))
+			print_xstats(i);
+		else if (!strcmp(res->op, "port"))
+			print_port(i);
+		printf("%s%s\n", border, border);
+	}
+}
+
+cmdline_parse_token_string_t cmd_show_all_show =
+	TOKEN_STRING_INITIALIZER(struct cmd_show_all_result, show_all,
+				 "show_all");
+cmdline_parse_token_string_t cmd_show_all_op =
+	TOKEN_STRING_INITIALIZER(struct cmd_show_all_result, op, "stats#port");
+
+cmdline_parse_inst_t cmd_show_all = {
+	.f = cmd_show_all_parsed,
+	.data = NULL,
+	.help_str = "show information for all ports",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_show_all_show,
+		(void *)&cmd_show_all_op,
+		NULL,
+	},
+};
+
+/****************/
+
+struct cmd_clear_result {
+	cmdline_fixed_string_t clear;
+	cmdline_fixed_string_t op;
+	uint8_t port_id;
+};
+
+static void
+cmd_clear_parsed(void *parsed_result,
+		 __rte_unused struct cmdline *cl,
+		 __rte_unused void *data)
+{
+	struct cmd_clear_result *res = parsed_result;
+
+	if (port_id_is_invalid(res->port_id))
+		return;
+
+	if (!strcmp(res->op, "stats")) {
+		app.mbuf_rx[res->port_id].pkts = 0;
+		rte_eth_xstats_reset(res->port_id);
+	}
+}
+
+cmdline_parse_token_string_t cmd_clear_clear =
+	TOKEN_STRING_INITIALIZER(struct cmd_clear_result, clear, "clear");
+cmdline_parse_token_string_t cmd_clear_op =
+	TOKEN_STRING_INITIALIZER(struct cmd_clear_result, op, "stats");
+cmdline_parse_token_num_t cmd_clear_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_clear_result, port_id, UINT8);
+
+cmdline_parse_inst_t cmd_clear = {
+	.f = cmd_clear_parsed,
+	.data = NULL,
+	.help_str = "clear information",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_clear_clear,
+		(void *)&cmd_clear_op,
+		(void *)&cmd_clear_port_id,
+		NULL,
+	},
+};
+
+/****************/
+
+struct cmd_clear_all_result {
+	cmdline_fixed_string_t clear_all;
+	cmdline_fixed_string_t op;
+};
+
+static void
+cmd_clear_all_parsed(void *parsed_result,
+		     __rte_unused struct cmdline *cl,
+		     __rte_unused void *data)
+{
+	struct cmd_clear_all_result *res = parsed_result;
+	unsigned int i;
+
+	for (i = 0; i < app.n_ports; i++) {
+		if (!strcmp(res->op, "stats")) {
+			app.mbuf_rx[i].pkts = 0;
+			rte_eth_xstats_reset(i);
+		}
+	}
+}
+
+cmdline_parse_token_string_t cmd_clear_all_clear =
+	TOKEN_STRING_INITIALIZER(struct cmd_clear_all_result, clear_all,
+				 "clear_all");
+cmdline_parse_token_string_t cmd_clear_all_op =
+	TOKEN_STRING_INITIALIZER(struct cmd_clear_all_result, op, "stats");
+
+cmdline_parse_inst_t cmd_clear_all = {
+	.f = cmd_clear_all_parsed,
+	.data = NULL,
+	.help_str = "clear information for all ports",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_clear_all_clear,
+		(void *)&cmd_clear_all_op,
+		NULL,
+	},
+};
+
+/****************/
+
+struct cmd_quit_result {
+	cmdline_fixed_string_t quit;
+};
+
+static void
+cmd_quit_parsed(__rte_unused void *parsed_result,
+		struct cmdline *cl,
+		__rte_unused void *data)
+{
+	cmdline_quit(cl);
+}
+
+cmdline_parse_token_string_t cmd_quit_quit =
+	TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit,
+				 "quit");
+
+cmdline_parse_inst_t cmd_quit = {
+	.f = cmd_quit_parsed,
+	.data = NULL,
+	.help_str = "exit application",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_quit_quit,
+		NULL,
+	},
+};
+
+/****************/
+
+cmdline_parse_ctx_t main_ctx[] = {
+	(cmdline_parse_inst_t *)&cmd_help,
+	(cmdline_parse_inst_t *)&cmd_show,
+	(cmdline_parse_inst_t *)&cmd_show_all,
+	(cmdline_parse_inst_t *)&cmd_clear,
+	(cmdline_parse_inst_t *)&cmd_clear_all,
+	(cmdline_parse_inst_t *)&cmd_quit,
+	(cmdline_parse_inst_t *)&cmd_add_del_cxgbe_flow_director,
+	NULL,
+};
diff --git a/examples/test-cxgbe-filters/commands.h b/examples/test-cxgbe-filters/commands.h
new file mode 100644
index 0000000..7503c92
--- /dev/null
+++ b/examples/test-cxgbe-filters/commands.h
@@ -0,0 +1,40 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015-2016 Chelsio Communications.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Chelsio Communications nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _COMMANDS_H_
+#define _COMMANDS_H_
+
+#include <cmdline_parse.h>
+
+extern cmdline_parse_inst_t cmd_add_del_cxgbe_flow_director;
+#endif /* _COMMANDS_H_ */
diff --git a/examples/test-cxgbe-filters/config.c b/examples/test-cxgbe-filters/config.c
new file mode 100644
index 0000000..d70eeba
--- /dev/null
+++ b/examples/test-cxgbe-filters/config.c
@@ -0,0 +1,79 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015-2016 Chelsio Communications.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Chelsio Communications nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include <rte_lcore.h>
+#include <rte_log.h>
+#include <rte_ethdev.h>
+
+#include "main.h"
+
+struct app_params app;
+
+int
+app_init_config(void)
+{
+	uint32_t n_lcores, lcore_id;
+
+	app.n_ports = rte_eth_dev_count();
+	if (!app.n_ports) {
+		RTE_LOG(ERR, USER1, "No probed ethernet devices\n");
+		return -1;
+	}
+
+	n_lcores = 0;
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (!rte_lcore_is_enabled(lcore_id))
+			continue;
+
+		if (lcore_id == rte_get_master_lcore())
+			continue;
+
+		if (n_lcores >= APP_MAX_PORTS || n_lcores >= app.n_ports)
+			break;
+
+		app.core_rx[n_lcores] = lcore_id;
+		n_lcores++;
+	}
+
+	if (n_lcores < 2) {
+		RTE_LOG(ERR, USER1, "Number of cores must be at least 3\n");
+		return -1;
+	} else if (n_lcores < app.n_ports) {
+		RTE_LOG(ERR, USER1, "Not enough cores for all ports\n");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/examples/test-cxgbe-filters/cxgbe/cxgbe_commands.c b/examples/test-cxgbe-filters/cxgbe/cxgbe_commands.c
new file mode 100644
index 0000000..f576589
--- /dev/null
+++ b/examples/test-cxgbe-filters/cxgbe/cxgbe_commands.c
@@ -0,0 +1,554 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015-2016 Chelsio Communications.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Chelsio Communications nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifndef __linux__
+#ifndef __FreeBSD__
+#include <net/socket.h>
+#else
+#include <sys/socket.h>
+#endif
+#endif
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse_etheraddr.h>
+#include <cmdline.h>
+
+#include <rte_common.h>
+#include <rte_byteorder.h>
+#include <rte_ethdev.h>
+#include <rte_malloc.h>
+
+#include "main.h"
+#include "commands.h"
+#include "cxgbe_fdir.h"
+
+/****************/
+
+static void
+ipv4_addr_to_uint(cmdline_ipaddr_t ip_addr, uint8_t *ip)
+{
+	do {
+		if (ip_addr.family == AF_INET) {
+			rte_memcpy(ip, &ip_addr.addr.ipv4.s_addr,
+				   sizeof(struct in_addr));
+		} else {
+			printf("invalid parameter.\n");
+			return;
+		}
+	} while (0);
+}
+
+static void
+ipv6_addr_to_array(cmdline_ipaddr_t ip_addr, uint8_t *ip)
+{
+	do {
+		if (ip_addr.family == AF_INET6) {
+			rte_memcpy(ip, &ip_addr.addr.ipv6,
+				   sizeof(struct in6_addr));
+		} else {
+			printf("invalid parameter.\n");
+			return;
+		}
+	} while (0);
+}
+
+/* *** cxgbe flow director *** */
+struct cmd_cxgbe_flow_director_result {
+	cmdline_fixed_string_t cxgbe_flow_director;
+	uint8_t port_id;
+	cmdline_fixed_string_t ops;
+
+	/* Match fields without mask */
+	cmdline_fixed_string_t flow_type;
+	cmdline_fixed_string_t flow_mode;
+	cmdline_fixed_string_t flow_mode_value;
+	cmdline_fixed_string_t flow_prio;
+
+	/* Match fields with mask */
+	cmdline_fixed_string_t iport;
+	uint8_t iport_value;
+	uint8_t iport_mask;
+	cmdline_fixed_string_t ether;
+	uint16_t ether_type;
+	uint16_t ether_type_mask;
+	cmdline_fixed_string_t vlan;
+	uint16_t ivlan;
+	uint16_t ivlan_mask;
+	uint16_t ovlan;
+	uint16_t ovlan_mask;
+	cmdline_fixed_string_t ip;
+	uint8_t tos;
+	uint8_t tos_mask;
+	uint8_t proto;
+	uint8_t proto_mask;
+	cmdline_ipaddr_t src_ip;
+	cmdline_ipaddr_t src_ip_mask;
+	cmdline_ipaddr_t dst_ip;
+	cmdline_ipaddr_t dst_ip_mask;
+	uint16_t src_port;
+	uint16_t src_port_mask;
+	uint16_t dst_port;
+	uint16_t dst_port_mask;
+
+	/* Action */
+	cmdline_fixed_string_t action;
+	cmdline_fixed_string_t queue;
+	uint16_t queue_id;
+
+	/* Action arguments */
+	cmdline_fixed_string_t port_op;
+	uint8_t eport;
+	cmdline_fixed_string_t ether_op;
+	struct ether_addr smac;
+	struct ether_addr dmac;
+	cmdline_fixed_string_t vlan_op;
+	uint16_t new_vlan;
+	cmdline_fixed_string_t nat_op;
+	cmdline_ipaddr_t nat_src_ip;
+	cmdline_ipaddr_t nat_dst_ip;
+	uint16_t nat_src_port;
+	uint16_t nat_dst_port;
+
+	cmdline_fixed_string_t fd_id;
+	uint32_t fd_id_value;
+};
+
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_cxgbe =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 cxgbe_flow_director,
+				 "filter");
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      port_id, UINT8);
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_ops =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 ops, "add#del");
+
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_flow_type =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 flow_type, "ipv4#ipv6");
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_flow_mode =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 flow_mode, "mode");
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_flow_mode_value =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 flow_mode_value, "maskfull#maskless");
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_flow_prio =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 flow_prio, "no-prio#prio");
+
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_iport =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 iport, "ingress-port");
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_iport_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      iport_value, UINT8);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_iport_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      iport_mask, UINT8);
+
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_ether =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 ether, "ether");
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_ether_type =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      ether_type, UINT16);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_ether_type_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      ether_type_mask, UINT16);
+
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_vlan =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 vlan, "vlan");
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_ivlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      ivlan, UINT16);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_ivlan_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      ivlan_mask, UINT16);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_ovlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      ovlan, UINT16);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_ovlan_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      ovlan_mask, UINT16);
+
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_ip =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 ip, "ip");
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_tos =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      tos, UINT8);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_tos_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      tos_mask, UINT8);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_proto =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      proto, UINT8);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_proto_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      proto_mask, UINT8);
+cmdline_parse_token_ipaddr_t cmd_cxgbe_flow_director_src_ip =
+	TOKEN_IPADDR_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 src_ip);
+cmdline_parse_token_ipaddr_t cmd_cxgbe_flow_director_src_ip_mask =
+	TOKEN_IPADDR_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 src_ip_mask);
+cmdline_parse_token_ipaddr_t cmd_cxgbe_flow_director_dst_ip =
+	TOKEN_IPADDR_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 dst_ip);
+cmdline_parse_token_ipaddr_t cmd_cxgbe_flow_director_dst_ip_mask =
+	TOKEN_IPADDR_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 dst_ip_mask);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_src_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      src_port, UINT16);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_src_port_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      src_port_mask, UINT16);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_dst_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      dst_port, UINT16);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_dst_port_mask =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      dst_port_mask, UINT16);
+
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_action =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 action, "drop#fwd#switch");
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_queue =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 queue, "queue");
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_queue_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      queue_id, UINT16);
+
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_port_op =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 port_op, "port-none#port-redirect");
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_eport =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      eport, UINT8);
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_ether_op =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 ether_op, "ether-none#mac-rewrite#mac-swap");
+cmdline_parse_token_etheraddr_t cmd_cxgbe_flow_director_smac =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				    smac);
+cmdline_parse_token_etheraddr_t cmd_cxgbe_flow_director_dmac =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				    dmac);
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_vlan_op =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 vlan_op, "vlan-none#vlan-rewrite#vlan-delete");
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_new_vlan =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      new_vlan, UINT16);
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_nat_op =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 nat_op, "nat-none#nat-rewrite");
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_nat_src_ip =
+	TOKEN_IPADDR_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 nat_src_ip);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_nat_dst_ip =
+	TOKEN_IPADDR_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 nat_dst_ip);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_nat_src_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      nat_src_port, UINT16);
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_nat_dst_port =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      nat_dst_port, UINT16);
+
+cmdline_parse_token_string_t cmd_cxgbe_flow_director_fd_id =
+	TOKEN_STRING_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+				 fd_id, "fd_id");
+cmdline_parse_token_num_t cmd_cxgbe_flow_director_fd_id_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_cxgbe_flow_director_result,
+			      fd_id_value, UINT32);
+
+static void
+cxgbe_fill_in_match_fields(struct rte_eth_fdir_input *input,
+			   struct cmd_cxgbe_flow_director_result *res)
+{
+	struct cxgbe_fdir_input_admin admin;
+	struct cxgbe_fdir_input_flow val, mask;
+	uint8_t *raw_pkt_flow;
+
+	memset(&admin, 0, sizeof(admin));
+	memset(&val, 0, sizeof(val));
+	memset(&mask, 0, sizeof(mask));
+
+	/* Fill in match admin fields that don't have masks */
+	if (!strcmp(res->flow_type, "ipv4"))
+		admin.type = 0;
+	else
+		admin.type = 1;
+
+	if (!strcmp(res->flow_mode_value, "maskfull"))
+		admin.cap = 0;
+	else
+		admin.cap = 1;
+
+	/*
+	 * If a packet can match both a maskfull and maskless filter,
+	 * enable prio bit to allow maskfull filter to have priority
+	 * over the maskless filter.
+	 */
+	if (!strcmp(res->flow_prio, "prio"))
+		admin.prio = 1;
+	else
+		admin.prio = 0;
+
+	/* Fill in match fields that have masks */
+	val.iport   = res->iport_value;
+	if (val.iport > 7) {
+		printf("iport must be < 8. Using 0\n");
+		val.iport = 0;
+	}
+	val.ethtype = rte_cpu_to_be_16(res->ether_type);
+
+	val.ivlan = rte_cpu_to_be_16(res->ivlan);
+	val.ovlan = rte_cpu_to_be_16(res->ovlan);
+
+	val.tos   = res->tos;
+	val.proto = res->proto;
+	if (!admin.type) {
+		ipv4_addr_to_uint(res->dst_ip, &val.lip[0]);
+		ipv4_addr_to_uint(res->src_ip, &val.fip[0]);
+	} else {
+		ipv6_addr_to_array(res->dst_ip, &val.lip[0]);
+		ipv6_addr_to_array(res->src_ip, &val.fip[0]);
+	}
+
+	val.lport = rte_cpu_to_be_16(res->dst_port);
+	val.fport = rte_cpu_to_be_16(res->src_port);
+
+	/* Fill in match mask fields */
+	mask.iport   = res->iport_mask;
+	if (mask.iport > 7) {
+		printf("iport_mask must be < 8. Using 0\n");
+		mask.iport = 0;
+	}
+	mask.ethtype = rte_cpu_to_be_16(res->ether_type_mask);
+
+	mask.ivlan = rte_cpu_to_be_16(res->ivlan_mask);
+	mask.ovlan = rte_cpu_to_be_16(res->ovlan_mask);
+
+	mask.tos   = res->tos_mask;
+	mask.proto = res->proto_mask;
+	if (!admin.type) {
+		ipv4_addr_to_uint(res->dst_ip_mask, &mask.lip[0]);
+		ipv4_addr_to_uint(res->src_ip_mask, &mask.fip[0]);
+	} else {
+		ipv6_addr_to_array(res->dst_ip_mask, &mask.lip[0]);
+		ipv6_addr_to_array(res->src_ip_mask, &mask.fip[0]);
+	}
+
+	mask.lport = rte_cpu_to_be_16(res->dst_port_mask);
+	mask.fport = rte_cpu_to_be_16(res->src_port_mask);
+
+	/* Fill in the above data to raw_pkt_flow array */
+	raw_pkt_flow = &input->flow.raw_pkt_flow[0];
+	rte_memcpy(raw_pkt_flow, &admin, sizeof(admin));
+	raw_pkt_flow += sizeof(admin);
+	rte_memcpy(raw_pkt_flow, &val, sizeof(val));
+	rte_memcpy(&input->flow_mask.raw_pkt_flow[0], &mask, sizeof(mask));
+}
+
+static void
+cxgbe_fill_in_action_fields(struct rte_eth_fdir_action *behavior,
+			    struct cmd_cxgbe_flow_director_result *res)
+{
+	struct cxgbe_fdir_action action;
+
+	memset(&action, 0, sizeof(action));
+
+	/* Fill in port behavior arguments */
+	if (!strcmp(res->port_op, "port-redirect")) {
+		action.eport = res->eport;
+		if (action.eport > 3) {
+			printf("eport must be < 4. Using 0\n");
+			action.eport = 0;
+		}
+	}
+
+	/* Fill in ether behavior arguments */
+	if (!strcmp(res->ether_op, "mac-rewrite")) {
+		rte_memcpy(&action.dmac, &res->dmac, sizeof(struct ether_addr));
+		rte_memcpy(&action.smac, &res->smac, sizeof(struct ether_addr));
+		action.newdmac = 1;
+		action.newsmac = 1;
+	} else if (!strcmp(res->ether_op, "mac-swap")) {
+		action.swapmac = 1;
+	}
+
+	/* Fill in vlan behavior arguments */
+	if (!strcmp(res->vlan_op, "vlan-delete")) {
+		action.vlan = rte_cpu_to_be_16(res->new_vlan);
+		action.newvlan = 1; /* VLAN delete */
+	} else if (!strcmp(res->vlan_op, "vlan-rewrite")) {
+		action.vlan = rte_cpu_to_be_16(res->new_vlan);
+		action.newvlan = 3; /* VLAN rewrite */
+	}
+
+	/* Fill in nat behavior arguments */
+	if (!strcmp(res->nat_op, "nat-rewrite")) {
+		action.nat_mode = 7; /* Rewrite IP and port */
+		if (!strcmp(res->flow_type, "ipv4")) {
+			ipv4_addr_to_uint(res->nat_dst_ip, &action.nat_lip[0]);
+			ipv4_addr_to_uint(res->nat_src_ip, &action.nat_fip[0]);
+		} else {
+			ipv6_addr_to_array(res->nat_dst_ip, &action.nat_lip[0]);
+			ipv6_addr_to_array(res->nat_src_ip, &action.nat_fip[0]);
+		}
+		action.nat_lport = rte_cpu_to_be_16(res->nat_dst_port);
+		action.nat_fport = rte_cpu_to_be_16(res->nat_src_port);
+	}
+
+	/* Fill in the above data to behavior_arg array */
+	rte_memcpy(&behavior->behavior_arg[0], &action, sizeof(action));
+}
+
+static void
+cmd_cxgbe_flow_director_parsed(void *parsed_result,
+			       __attribute__((unused)) struct cmdline *cl,
+			       __attribute__((unused)) void *data)
+{
+	struct cmd_cxgbe_flow_director_result *res = parsed_result;
+	struct rte_eth_fdir_filter entry;
+	struct rte_eth_dev_info dev_info;
+	int ret;
+
+	rte_eth_dev_info_get(res->port_id, &dev_info);
+	if (strcmp(dev_info.driver_name, "rte_cxgbe_pmd")) {
+		printf("Not a Chelsio Device\n");
+		return;
+	}
+
+	ret = rte_eth_dev_filter_supported(res->port_id, RTE_ETH_FILTER_FDIR);
+	if (ret < 0) {
+		printf("flow director is not supported on port %u.\n",
+		       res->port_id);
+		return;
+	}
+
+	memset(&entry, 0, sizeof(entry));
+	entry.input.flow_type = RTE_ETH_FLOW_RAW_PKT;
+
+	cxgbe_fill_in_match_fields(&entry.input, res);
+
+	if (!strcmp(res->action, "drop"))
+		entry.action.behavior = RTE_ETH_FDIR_REJECT;
+	else if (!strcmp(res->action, "fwd"))
+		entry.action.behavior = RTE_ETH_FDIR_ACCEPT;
+	else
+		entry.action.behavior = RTE_ETH_FDIR_SWITCH;
+
+	cxgbe_fill_in_action_fields(&entry.action, res);
+
+	entry.action.rx_queue = res->queue_id;
+	entry.soft_id = res->fd_id_value;
+	if (!strcmp(res->ops, "add"))
+		ret = rte_eth_dev_filter_ctrl(res->port_id, RTE_ETH_FILTER_FDIR,
+					      RTE_ETH_FILTER_ADD, &entry);
+	else
+		ret = rte_eth_dev_filter_ctrl(res->port_id, RTE_ETH_FILTER_FDIR,
+					      RTE_ETH_FILTER_DELETE, &entry);
+	if (ret < 0)
+		printf("flow director programming error: (%s)\n",
+		       strerror(-ret));
+}
+
+cmdline_parse_inst_t cmd_add_del_cxgbe_flow_director = {
+	.f = cmd_cxgbe_flow_director_parsed,
+	.data = NULL,
+	.help_str = "add or delete fdir entry on a cxgbe device",
+	.tokens = {
+		(void *)&cmd_cxgbe_flow_director_cxgbe,
+		(void *)&cmd_cxgbe_flow_director_port_id,
+		(void *)&cmd_cxgbe_flow_director_ops,
+		(void *)&cmd_cxgbe_flow_director_flow_type,
+		(void *)&cmd_cxgbe_flow_director_flow_mode,
+		(void *)&cmd_cxgbe_flow_director_flow_mode_value,
+		(void *)&cmd_cxgbe_flow_director_flow_prio,
+		(void *)&cmd_cxgbe_flow_director_iport,
+		(void *)&cmd_cxgbe_flow_director_iport_value,
+		(void *)&cmd_cxgbe_flow_director_iport_mask,
+		(void *)&cmd_cxgbe_flow_director_ether,
+		(void *)&cmd_cxgbe_flow_director_ether_type,
+		(void *)&cmd_cxgbe_flow_director_ether_type_mask,
+		(void *)&cmd_cxgbe_flow_director_vlan,
+		(void *)&cmd_cxgbe_flow_director_ivlan,
+		(void *)&cmd_cxgbe_flow_director_ivlan_mask,
+		(void *)&cmd_cxgbe_flow_director_ovlan,
+		(void *)&cmd_cxgbe_flow_director_ovlan_mask,
+		(void *)&cmd_cxgbe_flow_director_ip,
+		(void *)&cmd_cxgbe_flow_director_tos,
+		(void *)&cmd_cxgbe_flow_director_tos_mask,
+		(void *)&cmd_cxgbe_flow_director_proto,
+		(void *)&cmd_cxgbe_flow_director_proto_mask,
+		(void *)&cmd_cxgbe_flow_director_src_ip,
+		(void *)&cmd_cxgbe_flow_director_src_ip_mask,
+		(void *)&cmd_cxgbe_flow_director_dst_ip,
+		(void *)&cmd_cxgbe_flow_director_dst_ip_mask,
+		(void *)&cmd_cxgbe_flow_director_src_port,
+		(void *)&cmd_cxgbe_flow_director_src_port_mask,
+		(void *)&cmd_cxgbe_flow_director_dst_port,
+		(void *)&cmd_cxgbe_flow_director_dst_port_mask,
+		(void *)&cmd_cxgbe_flow_director_action,
+		(void *)&cmd_cxgbe_flow_director_queue,
+		(void *)&cmd_cxgbe_flow_director_queue_id,
+		(void *)&cmd_cxgbe_flow_director_port_op,
+		(void *)&cmd_cxgbe_flow_director_eport,
+		(void *)&cmd_cxgbe_flow_director_ether_op,
+		(void *)&cmd_cxgbe_flow_director_smac,
+		(void *)&cmd_cxgbe_flow_director_dmac,
+		(void *)&cmd_cxgbe_flow_director_vlan_op,
+		(void *)&cmd_cxgbe_flow_director_new_vlan,
+		(void *)&cmd_cxgbe_flow_director_nat_op,
+		(void *)&cmd_cxgbe_flow_director_nat_src_ip,
+		(void *)&cmd_cxgbe_flow_director_nat_dst_ip,
+		(void *)&cmd_cxgbe_flow_director_nat_src_port,
+		(void *)&cmd_cxgbe_flow_director_nat_dst_port,
+		(void *)&cmd_cxgbe_flow_director_fd_id,
+		(void *)&cmd_cxgbe_flow_director_fd_id_value,
+		NULL,
+	},
+};
diff --git a/examples/test-cxgbe-filters/cxgbe/cxgbe_fdir.h b/examples/test-cxgbe-filters/cxgbe/cxgbe_fdir.h
new file mode 100644
index 0000000..60219d8
--- /dev/null
+++ b/examples/test-cxgbe-filters/cxgbe/cxgbe_fdir.h
@@ -0,0 +1,79 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015-2016 Chelsio Communications.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Chelsio Communications nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _APP_CXGBE_FDIR_H_
+#define _APP_CXGBE_FDIR_H_
+
+/*
+ * RTE_ETH_FLOW_RAW_PKT representation.
+ * Must be kept in sync with the representation in
+ * drivers/net/cxgbe/cxgbe_fdir.h.
+ */
+struct cxgbe_fdir_input_admin {
+	uint8_t prio;        /* filter has priority over maskless */
+	uint8_t type;        /* 0 => IPv4, 1 => IPv6 */
+	uint8_t cap;         /* 0 => Maskfull, 1 => Maskless */
+};
+
+struct cxgbe_fdir_input_flow {
+	uint16_t ethtype;    /* ethernet type */
+	uint8_t iport;       /* ingress port */
+	uint8_t proto;       /* protocol type */
+	uint8_t tos;         /* TOS/Traffic Type */
+	uint16_t ivlan;      /* inner VLAN */
+	uint16_t ovlan;      /* outer VLAN */
+
+	uint8_t lip[16];     /* local IP address (IPv4 in [3:0]) */
+	uint8_t fip[16];     /* foreign IP address (IPv4 in [3:0]) */
+	uint16_t lport;      /* local port */
+	uint16_t fport;      /* foreign port */
+};
+
+struct cxgbe_fdir_action {
+	uint8_t eport;       /* egress port to switch packet out */
+	uint8_t newdmac;     /* rewrite destination MAC address */
+	uint8_t newsmac;     /* rewrite source MAC address */
+	uint8_t swapmac;     /* swap SMAC/DMAC for loopback packet */
+	uint8_t newvlan;     /* rewrite VLAN Tag */
+	uint8_t nat_mode;    /* specify NAT operation mode */
+
+	uint8_t dmac[ETHER_ADDR_LEN];  /* new destination MAC address */
+	uint8_t smac[ETHER_ADDR_LEN];  /* new source MAC address */
+	uint16_t vlan;       /* VLAN Tag to insert */
+
+	uint8_t nat_lip[16]; /* local IP to use after NAT'ing */
+	uint8_t nat_fip[16]; /* foreign IP to use after NAT'ing */
+	uint16_t nat_lport;  /* local port to use after NAT'ing */
+	uint16_t nat_fport;  /* foreign port to use after NAT'ing */
+};
+#endif /* _APP_CXGBE_FDIR_H_ */
diff --git a/examples/test-cxgbe-filters/init.c b/examples/test-cxgbe-filters/init.c
new file mode 100644
index 0000000..4452075
--- /dev/null
+++ b/examples/test-cxgbe-filters/init.c
@@ -0,0 +1,201 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015-2016 Chelsio Communications.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Chelsio Communications nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include <rte_log.h>
+#include <rte_mempool.h>
+#include <rte_ring.h>
+#include <rte_mbuf.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+
+#include "main.h"
+
+struct app_params app = {
+	/* Ports*/
+	.n_ports = APP_MAX_PORTS,
+	.port_rx_ring_size = 128,
+	.port_tx_ring_size = 512,
+
+	/* Buffer pool */
+	.pool_buffer_size = RTE_MBUF_DEFAULT_BUF_SIZE,
+	.pool_size = 32 * 1024,
+	.pool_cache_size = 256,
+
+	/* Burst sizes */
+	.burst_size_rx_read = 64,
+};
+
+static struct rte_eth_conf port_conf = {
+	.rxmode = {
+		.max_rx_pkt_len = ETHER_MAX_LEN,
+		.split_hdr_size = 0,
+		.header_split   = 0, /* Header Split disabled */
+		.hw_ip_checksum = 1, /* IP checksum offload enabled */
+		.hw_vlan_filter = 0, /* VLAN filtering disabled */
+		.jumbo_frame    = 0, /* Jumbo Frame Support disabled */
+		.hw_strip_crc   = 0, /* CRC stripped by hardware */
+	},
+	.fdir_conf = {
+		.mode = RTE_FDIR_MODE_NONE,
+		.pballoc = RTE_FDIR_PBALLOC_64K,
+		.status = RTE_FDIR_REPORT_STATUS,
+		.mask = {
+			.vlan_tci_mask = 0x0,
+			.ipv4_mask     = {
+				.src_ip = 0xFFFFFFFF,
+				.dst_ip = 0xFFFFFFFF,
+			},
+			.ipv6_mask     = {
+				.src_ip = {0xFFFFFFFF, 0xFFFFFFFF,
+					   0xFFFFFFFF, 0xFFFFFFFF},
+				.dst_ip = {0xFFFFFFFF, 0xFFFFFFFF,
+					   0xFFFFFFFF, 0xFFFFFFFF},
+			},
+			.src_port_mask = 0xFFFF,
+			.dst_port_mask = 0xFFFF,
+			.mac_addr_byte_mask = 0xFF,
+			.tunnel_type_mask = 1,
+			.tunnel_id_mask = 0xFFFFFFFF,
+		},
+		.drop_queue = 127,
+	},
+};
+
+static void
+app_init_mbuf_pools(void)
+{
+	/* Init the buffer pool */
+	app.pool = rte_pktmbuf_pool_create("mempool", app.pool_size,
+					   app.pool_cache_size, 0,
+					   app.pool_buffer_size,
+					   rte_socket_id());
+	if (!app.pool)
+		rte_panic("Cannot create mbuf pool\n");
+}
+
+static void
+app_ports_check_link(void)
+{
+	uint32_t all_ports_up, port, count, print_flag = 0;
+
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
+
+	for (count = 0; count < MAX_CHECK_TIME; count++) {
+		all_ports_up = 1;
+		for (port = 0; port < app.n_ports; port++) {
+			struct rte_eth_link link;
+
+			memset(&link, 0, sizeof(link));
+			rte_eth_link_get_nowait(port, &link);
+			if (print_flag == 1) {
+				RTE_LOG(INFO, USER1, "Port %u (%u Gbps) %s\n",
+					port, link.link_speed / 1000,
+					link.link_status ? "UP" : "DOWN");
+			}
+
+			if (!link.link_status) {
+				all_ports_up = 0;
+				break;
+			}
+		}
+
+		if (print_flag == 1)
+			break;
+
+		if (!all_ports_up) {
+			fflush(stdout);
+			rte_delay_ms(CHECK_INTERVAL);
+		}
+
+		if (all_ports_up || count == (MAX_CHECK_TIME - 1))
+			print_flag = 1;
+	}
+}
+
+static void
+app_init_ports(void)
+{
+	struct ether_addr mac_addr;
+	unsigned int  port;
+	int ret;
+
+	/* Init NIC ports, then start the ports */
+	for (port = 0; port < app.n_ports; port++) {
+		RTE_LOG(INFO, USER1, "Initializing NIC port %u ...\n", port);
+
+		/* Init port */
+		ret = rte_eth_dev_configure(port, 1, 1,	&port_conf);
+		if (ret < 0)
+			rte_panic("Cannot init NIC port %u (%d)\n", port, ret);
+
+		/* Init RX queues */
+		ret = rte_eth_rx_queue_setup(port, 0, app.port_rx_ring_size,
+					     rte_eth_dev_socket_id(port), NULL,
+					     app.pool);
+		if (ret < 0)
+			rte_panic("Cannot init RX for port %u (%d)\n",
+				  port, ret);
+
+		/* Init TX queues */
+		ret = rte_eth_tx_queue_setup(port, 0, app.port_tx_ring_size,
+					     rte_eth_dev_socket_id(port), NULL);
+		if (ret < 0)
+			rte_panic("Cannot init TX for port %u (%d)\n",
+				  port, ret);
+
+		/* Start port */
+		ret = rte_eth_dev_start(port);
+		if (ret < 0)
+			rte_panic("Cannot start port %u (%d)\n", port, ret);
+
+		rte_eth_macaddr_get(port, &mac_addr);
+		printf("Port %d: %02X:%02X:%02X:%02X:%02X:%02X\n", port,
+		       mac_addr.addr_bytes[0], mac_addr.addr_bytes[1],
+		       mac_addr.addr_bytes[2], mac_addr.addr_bytes[3],
+		       mac_addr.addr_bytes[4], mac_addr.addr_bytes[5]);
+
+		rte_eth_promiscuous_enable(port);
+	}
+
+	app_ports_check_link();
+}
+
+void
+app_init(void)
+{
+	app_init_mbuf_pools();
+	app_init_ports();
+}
diff --git a/examples/test-cxgbe-filters/main.c b/examples/test-cxgbe-filters/main.c
new file mode 100644
index 0000000..c753b32
--- /dev/null
+++ b/examples/test-cxgbe-filters/main.c
@@ -0,0 +1,79 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015-2016 Chelsio Communications.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Chelsio Communications nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include <rte_eal.h>
+#include <rte_common.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+#include "main.h"
+
+int
+main(int argc, char **argv)
+{
+	struct cmdline *cl;
+	unsigned int i;
+	int ret;
+
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		return -1;
+
+	if (app_init_config() < 0)
+		return -1;
+
+	app_init();
+
+	for (i = 0; i < app.n_ports; i++) {
+		/* Launch rx main loop on every lcore except master */
+		rte_eal_remote_launch(app_main_loop_rx, NULL, app.core_rx[i]);
+	}
+
+	cl = cmdline_stdin_new(main_ctx, "cxgbe> ");
+	if (!cl) {
+		printf("cmdline init failed\n");
+		return -1;
+	}
+
+	cmdline_interact(cl);
+	cmdline_stdin_exit(cl);
+
+	return 0;
+}
diff --git a/examples/test-cxgbe-filters/main.h b/examples/test-cxgbe-filters/main.h
new file mode 100644
index 0000000..4252052
--- /dev/null
+++ b/examples/test-cxgbe-filters/main.h
@@ -0,0 +1,77 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015-2016 Chelsio Communications.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Chelsio Communications nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CXGBE_APP_MAIN_H_
+#define _CXGBE_APP_MAIN_H_
+
+#include <cmdline_parse.h>
+
+#define APP_MBUF_ARRAY_SIZE 256
+#define APP_MAX_PORTS 4
+
+struct app_mbuf_array {
+	struct rte_mbuf *array[APP_MBUF_ARRAY_SIZE];
+	uint64_t pkts; /* Number of received packets */
+};
+
+struct app_params {
+	/* CPU cores */
+	uint32_t core_rx[APP_MAX_PORTS];
+
+	/* Ports*/
+	uint32_t n_ports;
+	uint32_t port_rx_ring_size;
+	uint32_t port_tx_ring_size;
+
+	/* Internal buffers */
+	struct app_mbuf_array mbuf_rx[APP_MAX_PORTS];
+
+	/* Buffer pool */
+	struct rte_mempool *pool;
+	uint32_t pool_buffer_size;
+	uint32_t pool_size;
+	uint32_t pool_cache_size;
+
+	/* Burst sizes */
+	uint32_t burst_size_rx_read;
+} __rte_cache_aligned;
+
+extern struct app_params app;
+
+extern cmdline_parse_ctx_t main_ctx[];
+
+int app_init_config(void);
+void app_init(void);
+
+int app_main_loop_rx(void *arg);
+#endif /* _CXGBE_APP_MAIN_H_ */
diff --git a/examples/test-cxgbe-filters/runtime.c b/examples/test-cxgbe-filters/runtime.c
new file mode 100644
index 0000000..043b5a4
--- /dev/null
+++ b/examples/test-cxgbe-filters/runtime.c
@@ -0,0 +1,74 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015-2016 Chelsio Communications.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Chelsio Communications nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rte_log.h>
+#include <rte_ethdev.h>
+
+#include "main.h"
+
+int
+app_main_loop_rx(void *arg)
+{
+	uint32_t lcore_id;
+	uint16_t i;
+	uint8_t port_id = 0;
+
+	RTE_SET_USED(arg);
+
+	lcore_id = rte_lcore_id();
+
+	for (i = 0; i < app.n_ports; i++)
+		if (app.core_rx[i] == lcore_id)
+			port_id = i;
+
+	app.mbuf_rx[port_id].pkts = 0;
+
+	RTE_LOG(INFO, USER1, "Core %u is doing RX for port %d\n",
+		lcore_id, port_id);
+
+	while (1) {
+		uint16_t n_mbufs;
+
+		n_mbufs = rte_eth_rx_burst(port_id, 0,
+					   app.mbuf_rx[port_id].array,
+					   app.burst_size_rx_read);
+
+		if (!n_mbufs)
+			continue;
+
+		app.mbuf_rx[port_id].pkts += n_mbufs;
+		for (i = 0; i < n_mbufs; i++)
+			rte_pktmbuf_free(app.mbuf_rx[port_id].array[i]);
+	}
+	return 0;
+}
