From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from stargate3.asicdesigners.com (stargate.chelsio.com [12.32.117.8]) by dpdk.org (Postfix) with ESMTP id 908E795DE for ; Wed, 3 Feb 2016 09:32:45 +0100 (CET) Received: from localhost (scalar.blr.asicdesigners.com [10.193.185.94]) by stargate3.asicdesigners.com (8.13.8/8.13.8) with ESMTP id u138Wfd9024895; Wed, 3 Feb 2016 00:32:42 -0800 From: Rahul Lakkireddy To: dev@dpdk.org Date: Wed, 3 Feb 2016 14:02:23 +0530 Message-Id: <2131fd9284f6b25cdbb4c7885354856f4ff2ebe5.1454408702.git.rahul.lakkireddy@chelsio.com> X-Mailer: git-send-email 2.5.3 In-Reply-To: References: In-Reply-To: References: Cc: Kumar Sanghvi , Nirranjan Kirubaharan Subject: [dpdk-dev] [PATCH 02/10] examples/test-cxgbe-filters: add example to test cxgbe fdir support X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 03 Feb 2016 08:32:46 -0000 Add a new test_cxgbe_filters command line example to test support for Chelsio T5 hardware filtering. Shows how to pass the Chelsio input flow and input masks. Also, shows how to pass extra behavior arguments to rewrite fields in matched filter rules. Also add documentation and update MAINTAINERS. Signed-off-by: Rahul Lakkireddy Signed-off-by: Kumar Sanghvi --- MAINTAINERS | 2 + doc/guides/sample_app_ug/index.rst | 1 + doc/guides/sample_app_ug/test_cxgbe_filters.rst | 694 +++++++++++++++++++++ examples/Makefile | 1 + examples/test-cxgbe-filters/Makefile | 63 ++ examples/test-cxgbe-filters/commands.c | 429 +++++++++++++ examples/test-cxgbe-filters/commands.h | 40 ++ examples/test-cxgbe-filters/config.c | 79 +++ examples/test-cxgbe-filters/cxgbe/cxgbe_commands.c | 554 ++++++++++++++++ examples/test-cxgbe-filters/cxgbe/cxgbe_fdir.h | 79 +++ examples/test-cxgbe-filters/init.c | 201 ++++++ examples/test-cxgbe-filters/main.c | 79 +++ examples/test-cxgbe-filters/main.h | 77 +++ examples/test-cxgbe-filters/runtime.c | 74 +++ 14 files changed, 2373 insertions(+) create mode 100644 doc/guides/sample_app_ug/test_cxgbe_filters.rst create mode 100644 examples/test-cxgbe-filters/Makefile create mode 100644 examples/test-cxgbe-filters/commands.c create mode 100644 examples/test-cxgbe-filters/commands.h create mode 100644 examples/test-cxgbe-filters/config.c create mode 100644 examples/test-cxgbe-filters/cxgbe/cxgbe_commands.c create mode 100644 examples/test-cxgbe-filters/cxgbe/cxgbe_fdir.h create mode 100644 examples/test-cxgbe-filters/init.c create mode 100644 examples/test-cxgbe-filters/main.c create mode 100644 examples/test-cxgbe-filters/main.h create mode 100644 examples/test-cxgbe-filters/runtime.c diff --git a/MAINTAINERS b/MAINTAINERS index b90aeea..1785b02 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -261,6 +261,8 @@ Chelsio cxgbe M: Rahul Lakkireddy 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 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 +`_. + +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 +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 + +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 + +#include +#include +#include + +#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 +#include +#ifndef __linux__ +#ifndef __FreeBSD__ +#include +#else +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + +#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 +#include + +#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; +} -- 2.5.3