From patchwork Wed Oct 25 17:58:27 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ori Kam X-Patchwork-Id: 30901 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 44B951B81C; Wed, 25 Oct 2017 19:58:59 +0200 (CEST) Received: from EUR02-VE1-obe.outbound.protection.outlook.com (mail-eopbgr20064.outbound.protection.outlook.com [40.107.2.64]) by dpdk.org (Postfix) with ESMTP id A010F1B1B2 for ; Wed, 25 Oct 2017 19:58:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Mellanox.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=NXu7fGjsC8zBhA3Fx3eLioFXLPz/g4h250X68MMmmno=; b=vR5dp7+wSeb5Xf3pXtIr9Xt9WGchVHYkOGY3pJYSVabuHX1Hwx2a5L1O1VCYFuZUjjIUSnlBVnkhtRtfXZ0IPSqPofiDqdiSBooe4+nBc1cOE4+aRPnd9c5hA2JCbnTZtyIuLgE/nAJvrkQyyxA1VFAJsCbbjMhSWYSVUETu6Oo= Received: from localhost.localdomain (82.166.227.17) by AM4PR05MB3201.eurprd05.prod.outlook.com (2603:10a6:205:3::30) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.156.4; Wed, 25 Oct 2017 17:58:54 +0000 From: Ori Kam To: adrien.mazarguil@6wind.com, john.mcnamara@intel.com, thomas@monjalon.net Cc: dev@dpdk.org, orika@mellanox.com Date: Wed, 25 Oct 2017 20:58:27 +0300 Message-Id: <28eeef90ebaf5e8afb31837fade1b36c484f29a5.1508953769.git.orika@mellanox.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1508407471-6252-1-git-send-email-orika@mellanox.com> References: <1508407471-6252-1-git-send-email-orika@mellanox.com> MIME-Version: 1.0 X-Originating-IP: [82.166.227.17] X-ClientProxiedBy: AM5PR0701CA0004.eurprd07.prod.outlook.com (2603:10a6:203:51::14) To AM4PR05MB3201.eurprd05.prod.outlook.com (2603:10a6:205:3::30) X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 0dbccf27-53b5-48cd-2ca2-08d51bd20edc X-MS-Office365-Filtering-HT: Tenant X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(22001)(48565401081)(4534020)(4602075)(4627075)(201703031133081)(201702281549075)(2017052603238); SRVR:AM4PR05MB3201; X-Microsoft-Exchange-Diagnostics: 1; AM4PR05MB3201; 3:3S9fBCMvX8A6yCZNOP+7qhAmJNmJijpskDptzkiaRzehRFuhP9JTRWljpWRgkmSXmSINFw80Mx40HYIxn/KahMErn2rK1Oo7CB3pOI7DZxbNOn0uy3q/d1OQG/SfHqX6X0YqpO0yBXMdPBRDooYCSO3zDBTWIEQNd36kuQD2xMoPZNVsBRzZHJRB/6ggBY4hP6OOkGId2d+gYIVyMOG0srj/5RVXytBml/fDFSEfRPgzHyWPH8rZ7fF1yumEck00; 25:vJmhEDeO0GhbJxIROoOMAaP03dqVQDoC6TokbIgkocMqO/9O+PwTWSCuWUVOBIy+tVXnisvMb8HMnn1QrOvffCtyq6bS4dgmtC0PO/e4VwQbfKtdTArf9a3PvWRY33ePDatWegsRgJnAcn6ufR0Y0yKVdy3347LqAtQ55ZrPgPUO5lMQpBhJsCMbT6aD05lju4EZuV9pwjKUaBZOIS+Uauv5CiHnFud5CbIY25QBFnw8eWOEXvRvrypgvD9J4D33sokWsN1MNnyIF38QkX4Ru46lrv8vXNMEfNKgSEjsY0kAgMFVAGz9pczrzhMXKrRKxt8S0F8aJSC8snV7PrJeuA==; 31:p9J/iSs2RaPJliqf7EVlNtA4XsYV5Jl7rLSb1lely7LjYYvo4GQ+k53JnHz9s2EijcEpvAUk6xKtvE7WxJMno/88kcUSRFbzWa99anjTrdbOQaPmsA9kmuEx8sUXIiGV7YTy3F/bfm3UbuqwcabVDSTXvY+TYrdXBnbXLJCJhj827DGdNWOYgioJp7ax1TZJU2dhuHPz9HZEV/ctfkAQg6USMUpkrSO4CwJ1sX5YDcc= X-MS-TrafficTypeDiagnostic: AM4PR05MB3201: Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=orika@mellanox.com; X-LD-Processed: a652971c-7d2e-4d9b-a6a4-d149256f461b,ExtAddr X-Microsoft-Exchange-Diagnostics: 1; AM4PR05MB3201; 20:Tg2GX+SHFhz8bJKeeFrbLPC4u11lYeUKFPDMiwR1V/BjkRNdkN69IndBfP43a8fQkEecG1L5YQNVZpB2EcitfimF1NJH4YlPy9Jub+BQzgQXiQGx4pZdP4hU83FqHquvk8c6l10PsHa33AdcLMZDLtUaYQOGz8STQTY9K7W6+1ILxEzt8iYFX+xXtduQhfUGg2SDGi2cCzUC7klmZpFI4x6qId6p3ovrE/CpSzYOnHUa0dHbmBAMmqpJC55AN4DkLow08ftOY235W97mJjgTDdSFqhrNEqxq7Y2djgBRCTqrdiKOhi07czoJo/51KtVnG/4GrZiH02CQRe+gSd8hhN9tNux+Uu+lDXRB9SBB91PbQO7bRJygt6tRE1t6Negz+N737jjX5CKkv/EygJR1KvK7CHVoRDpFwuMVYODtlt/yi59O+zRwnDWM4imumW/8QWR6T+eadni0qZLtsLO2znOP3H6p0Wsa0LjOs+coK+Qhy1Zsf8N3yNzF3/sbkRl7; 4:x41XUmVi0nere2P/H0V1YoQz/m+tTB0jG8GwvFgIgKbEqnbVe38G+hz3BUBPxP3pM0Dxp9E4tw0Y14BJ0LJIdOwow85yXN/UauHbwvPsb3Pyqnv+ruSieIP4w7gY+X50FIWHkOyqgI5iR/tty3cwWeul7X6aTjgSxQyQ3WTXOZC50endhtPHK/r2F0aAkhlF6ns1sUCnTg2TARZbWB9Yfy1/y5EMdGYMcqM6K5LH7+9Ds2li9TJTv2URiAkW5x1zmrt1knDXoe9aYHeIsbijxEd+X9+L8nl67aZS9OYKzLS7fBq07RBrzq+DPG2tL7qYY9113iSzV5RJBuDIirCnN34DiavoYtfj8ux0w33+dWWBOl/yfnnkjKRjiOuHxJBi9bziME4S1ixXmclg0W47yA== X-Exchange-Antispam-Report-Test: UriScan:(278428928389397)(200054503718035)(116097685857584)(211171220733660); X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(100000700101)(100105000095)(100000701101)(100105300095)(100000702101)(100105100095)(6040450)(2401047)(5005006)(8121501046)(100000703101)(100105400095)(3231020)(93006095)(93001095)(10201501046)(3002001)(6055026)(6041248)(20161123564025)(20161123560025)(201703131423075)(201702281528075)(201703061421075)(201703061406153)(20161123558100)(20161123562025)(20161123555025)(6072148)(201708071742011)(100000704101)(100105200095)(100000705101)(100105500095); SRVR:AM4PR05MB3201; BCL:0; PCL:0; RULEID:(100000800101)(100110000095)(100000801101)(100110300095)(100000802101)(100110100095)(100000803101)(100110400095)(100000804101)(100110200095)(100000805101)(100110500095); SRVR:AM4PR05MB3201; X-Forefront-PRVS: 0471B73328 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10009020)(6069001)(6009001)(376002)(346002)(39860400002)(51234002)(199003)(189002)(305945005)(107886003)(33646002)(106356001)(6506006)(16586007)(5003940100001)(6486002)(118296001)(316002)(6512007)(50226002)(105586002)(97736004)(76176999)(53936002)(2906002)(68736007)(53946003)(50986999)(101416001)(4326008)(16526018)(47776003)(86362001)(66066001)(81166006)(50466002)(3846002)(48376002)(36756003)(6116002)(7736002)(8936002)(189998001)(25786009)(8676002)(478600001)(81156014)(6666003)(5660300001)(2950100002); DIR:OUT; SFP:1101; SCL:1; SRVR:AM4PR05MB3201; H:localhost.localdomain; FPR:; SPF:None; PTR:InfoNoRecords; A:1; MX:1; LANG:en; Received-SPF: None (protection.outlook.com: mellanox.com does not designate permitted sender hosts) X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; AM4PR05MB3201; 23:UvyTOaR8R2eGbPegDDynMzptkJUwrGw6GtjRfR0PN?= E0fZ1BFADN2ICNyy3j8kZrbFyOFe1GohStYiZiMWr68Erxs3lJ7W7FxfQxkl2uvWrJ62ecJA+Z6MWLdXwsut/Zq4byj1Dib+2U5bVcsCFm+2Ti1RcAAOVByJ8Btbd8Z4oVTaRaOSVCr5iSOBnvDxIcGDUWcr2xsKk7KSomBcdOYtgh448KQdqUtSIHSdX3wvPHyeCC9JJj3Qq8KfknoYwH7pv+9o5B6PdU2Vwm9iHG4klCCEpYDeI5CEvoQJMtRPxcHg5FixeElFX4p9eSzhw9iXa395YniNGTTQTLREz/HPL297Pz1GAXEVgAsgj7LfhJJGo7mtuhpuqSQWGjTAQqqjPzyo0kf/PJ7KNMa9k07jgdXrTUVqEo+Xt6+qyJT2qgRxovxfYhLE5/zcPN9QwboGAw0Cz8zfGTY/85C43ycu8otJXeNXqMhRQvf1Yl0/I6nPQpV0HhA4ZoDBad9ow86DYp+tC9tcDt1bBscyawDjN6O+JjcY8V2U74Y+ACjYVyZAX/AVYBhGMzvPp6A/hAsAme45zXCfTOpW3HW1id5GhHInivQRA/wKw7Mgi+Byrej8T0v72Tz5N89AXx0XOE+3xofeEZc0ztCM8klM6JlaDW7LeNHaVascYYUuMeawbV+HESfQOUwkgiwS14vuH4LFVzmvA5M5XbkbqWL/6fmeBFRVaRVuO4PNjXV6tRbbL9Wxg2z9yf6wUTtoD0EjK/pdZrfEBAhmQiVbWS6m6fR5io6nRfJJuYba28E3iOYRet1p/c4g3VmF9E+6V4pPo2rj2aXu77mu0+E2YSIMl26+tNBpMt4Db/ZB7Uwu86gBZ9+asOet9xjKDYIsJRU4kTR97qyWuGKHIUOlnBr9ODq14ySRgFFApZAUwf0TE7x1/sZjwVFlolmBMt9f9ljw5olDap86MgHY993hep2SJclomzX2j1MujQWUM6XDihk481LwZt0OrGlWLLmZlURYy5sFXdWySIK+RfiQYu/UaVDa1s6Ncy/PFxeWGXpExBX9togTqZndNvVE+UckG44F+a3CsB1cQaAXkTXJs8c9pEFiHw3djJjuOpJPXzlvqeJGwwapd+prQhBJU38dKtHXBP8KP3nF5N9Vw1y07fd9rAjuJr+1fCl4q/w44UjFMcyfLM= X-Microsoft-Exchange-Diagnostics: 1; AM4PR05MB3201; 6:LqisauA73Ig5T+Fq2SNB605W/+GHHbs1AseGEZukGFAxVrsJzbEOJbyiwMgGrSLG/sT2CV3Y0BViiledvgIq5qnTMcAcOwcZT4cHsseeaYO9IpeIpaDCPg2wmDov7HJA8AxG7JNoampJ9EASp8S//FctWQ8ydzCWYzvr9rUrb2htGWUxRgx3Xf/omB0guTVpdKHz0/fMB1qSDsckqJNOlvJD9h3mkGVjFOzvkCCqaUT2DXlU7UJjArEg3bOxhQNPYoQvYCo4sR1+gmX7Y+1+DJr4qNW5POAkwrM5E5W/f4iBjEeCuc0Jyya4ONxqPOuZTdpEcHBvjW+esqsaBulS+w==; 5:OLHRJfcEYTLg9eYi7DEk4Jx6+/g0y/jWi6b83SUdSMuWpuo6o2kzWwY75k0fnNyVdJQr7TFRxL5hziqUb6cKCSmNXYWl5pJrNRQUI+Wu4a8rKbciAkGIaa+Xbk8wGmsk9VxWJsCWzv/b0P7CiClsFg==; 24:Hq9cSqtk+v2rr12BiOHCguioNXingM/MrCZpDDxjAvcKldBLWc9Zlictt68l2dBBRN4sfUnmqxA1fxtROcUaJrNexGqN7IAGxAmX48HwPJc=; 7:zNbEGfvZFjG8S2nsMIZWobA2G23fJnewF7yShZp9pzH4xjIehTzhfUJ19e+ejK5RqRrXLksrVF3cfO8OnZIc5uBAyg5q0WBJuWz2/SbGJFUislpUtyPEC8Y/6+OJAnAYNA3esTtX7lP2BIGxoQzyKX+/hLJBICOLP9jLrGQe9ZDe57YRt8s+spusc9KOB7Hd1MV8mhoSLhKpkTXKPpGT0d7k0B3jp1jmCTXHIxpmZko= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: Mellanox.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 25 Oct 2017 17:58:54.2935 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 0dbccf27-53b5-48cd-2ca2-08d51bd20edc X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: a652971c-7d2e-4d9b-a6a4-d149256f461b X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM4PR05MB3201 Subject: [dpdk-dev] [PATCH v2] examples/flow_filtering: demo of simple rte flow X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" This application shows a simple usage of the rte_flow API for hardware filtering offloading. In this demo we are filtering specific IP to specific target queue, while sending all the rest of the packets to other queue. Signed-off-by: Ori Kam --- v2: * Merge patches into one patch. * Add flow_filtering doc to index. * Add example to Makefile. * Small fixes. * Remove cover letter. MAINTAINERS | 4 + doc/guides/sample_app_ug/flow_filtering.rst | 540 ++++++++++++++++++++++++++++ doc/guides/sample_app_ug/index.rst | 1 + examples/Makefile | 1 + examples/flow_filtering/Makefile | 17 + examples/flow_filtering/flow_blocks.c | 150 ++++++++ examples/flow_filtering/main.c | 244 +++++++++++++ 7 files changed, 957 insertions(+) create mode 100644 doc/guides/sample_app_ug/flow_filtering.rst create mode 100644 examples/flow_filtering/Makefile create mode 100644 examples/flow_filtering/flow_blocks.c create mode 100644 examples/flow_filtering/main.c diff --git a/MAINTAINERS b/MAINTAINERS index 2a58378..ad9179b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -978,3 +978,7 @@ F: examples/tep_termination/ F: examples/vmdq/ F: examples/vmdq_dcb/ F: doc/guides/sample_app_ug/vmdq_dcb_forwarding.rst + +M: Ori Kam +F: examples/flow_filtering/ +F: doc/guides/sample_app_ug/flow_filtering.rst diff --git a/doc/guides/sample_app_ug/flow_filtering.rst b/doc/guides/sample_app_ug/flow_filtering.rst new file mode 100644 index 0000000..1122d2f --- /dev/null +++ b/doc/guides/sample_app_ug/flow_filtering.rst @@ -0,0 +1,540 @@ +.. BSD LICENSE + Copyright(c) 2017 Mellanox Corporation. All rights reserved. + 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 Mellanox Corporation 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. + + +Basic RTE Flow Filtering Sample Application +=========================================== + +The Basic RTE flow filtering sample application is a simple example of a +creating a RTE flow rule. + +It is intended as a demonstration of the basic components RTE flow rules. + + +Compiling the Application +------------------------- + +To compile the application export the path to the DPDK source tree and go to +the example directory: + +.. code-block:: console + + export RTE_SDK=/path/to/rte_sdk + + cd ${RTE_SDK}/examples/flow_filtering + +Set the target, 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 +----------------------- + +To run the example in a ``linuxapp`` environment: + +.. code-block:: console + + ./build/flow -l 1 -n 1 + +Refer to *DPDK Getting Started Guide* for general information on running +applications and the Environment Abstraction Layer (EAL) options. + + +Explanation +----------- + +The example is build from 2 main files, +``main.c`` which holds the example logic and ``flow_blocks.c`` that holds the +implementation for building the flow rule. + +The following sections provide an explanation of the main components of the +code. + +All DPDK library functions used in the sample code are prefixed with ``rte_`` +and are explained in detail in the *DPDK API Documentation*. + + +The Main Function +~~~~~~~~~~~~~~~~~ + +The ``main()`` function located in ``main.c`` file performs the initialization +and runs the main loop function. + +The first task is to initialize the Environment Abstraction Layer (EAL). The +``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()`` +function. The value returned is the number of parsed arguments: + +.. code-block:: c + + int ret = rte_eal_init(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); + + +The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers) +used by the application: + +.. code-block:: c + + mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", 4096, 128, 0, + RTE_MBUF_DEFAULT_BUF_SIZE, + rte_socket_id()); + +Mbufs are the packet buffer structure used by DPDK. They are explained in +detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*. + +The ``main()`` function also initializes all the ports using the user defined +``init_port()`` function which is explained in the next section: + +.. code-block:: c + + init_port(); + +Once the initialization is complete, the application is ready to launch the +``main_loop()`` function. Which is explained below. + + +.. code-block:: c + + main_loop(); + +The Port Initialization Function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The main functional part of the port initialization used in the flow filtering +application is shown below: + +.. code-block:: c + + init_port(void) + { + int ret; + uint16_t i; + struct rte_flow_error error; + struct rte_eth_conf port_conf = { + .rxmode = { + .split_hdr_size = 0, + /**< Header Split disabled */ + .header_split = 0, + /**< IP checksum offload disabled */ + .hw_ip_checksum = 0, + /**< VLAN filtering disabled */ + .hw_vlan_filter = 0, + /**< Jumbo Frame Support disabled */ + .jumbo_frame = 0, + /**< CRC stripped by hardware */ + .hw_strip_crc = 1, + }, + }; + + printf(":: initializing port: %d\n", port_id); + ret = rte_eth_dev_configure(port_id, + nr_queues, nr_queues, &port_conf); + if (ret < 0) { + rte_exit(EXIT_FAILURE, + ":: cannot configure device: err=%d, port=%u\n", + ret, port_id); + } + + /* only set Rx queues: something we care only so far */ + for (i = 0; i < nr_queues; i++) { + ret = rte_eth_rx_queue_setup(port_id, i, 512, + rte_eth_dev_socket_id(port_id), + NULL, + mbuf_pool); + if (ret < 0) { + rte_exit(EXIT_FAILURE, + ":: Rx queue setup failed: err=%d, port=%u\n", + ret, port_id); + } + } + + /* create flow for send packet with */ + flow = generate_ipv4_flow(port_id, selected_queue, + SRC_IP, EMPTY_MASK, + DEST_IP, FULL_MASK, &error); + if (!flow) { + printf("Flow can't be created %d message: %s\n", + error.type, + error.message ? error.message : "(no stated reason)"); + rte_exit(EXIT_FAILURE, "error in creating flow"); + } + + rte_eth_promiscuous_enable(port_id); + + ret = rte_eth_dev_start(port_id); + if (ret < 0) { + rte_exit(EXIT_FAILURE, + "rte_eth_dev_start:err=%d, port=%u\n", + ret, port_id); + } + + assert_link_status(); + + printf(":: initializing port: %d done\n", port_id); + } + +The Ethernet port is configured with default settings using the +``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct: + +.. code-block:: c + + struct rte_eth_conf port_conf = { + .rxmode = { + .split_hdr_size = 0, + /**< Header Split disabled */ + .header_split = 0, + /**< IP checksum offload disabled */ + .hw_ip_checksum = 0, + /**< VLAN filtering disabled */ + .hw_vlan_filter = 0, + /**< Jumbo Frame Support disabled */ + .jumbo_frame = 0, + /**< CRC stripped by hardware */ + .hw_strip_crc = 1, + }, + }; + + ret = rte_eth_dev_configure(port_id, nr_queues, nr_queues, &port_conf); + if (ret < 0) { + rte_exit(EXIT_FAILURE, + ":: cannot configure device: err=%d, port=%u\n", + ret, port_id); + } + +For this example we are configuring number of rx queues that are connected to +a single port. + +.. code-block:: c + + for (i = 0; i < nr_queues; i++) { + ret = rte_eth_rx_queue_setup(port_id, i, 512, + rte_eth_dev_socket_id(port_id), + NULL, + mbuf_pool); + if (ret < 0) { + rte_exit(EXIT_FAILURE, + ":: Rx queue setup failed: err=%d, port=%u\n", + ret, port_id); + } + } + +In the next step we create and apply the flow rule. which is to send packets with +destination ip equals to 192.168.1.1 to queue number 1. The detail +explanation of the ``generate_ipv4_flow()`` appears later in this document: + +.. code-block:: c + + flow = generate_ipv4_flow(port_id, selected_queue, + SRC_IP, EMPTY_MASK, + DEST_IP, FULL_MASK, &error); + +We are setting the RX port to promiscuous mode: + +.. code-block:: c + + rte_eth_promiscuous_enable(port_id); + +The last step is to start the port. + +.. code-block:: c + + ret = rte_eth_dev_start(port_id); + if (ret < 0) { + rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err%d, port=%u\n", + ret, port_id); + } + + +The main_loop function +~~~~~~~~~~~~~~~~~~~~~~ + +As we saw above the ``main()`` function calls an application function to handle +the main loop. For the flow filtering application the main_loop function +looks like the following: + +.. code-block:: c + + static void + main_loop(void) + { + struct rte_mbuf *mbufs[32]; + struct ether_hdr *eth_hdr; + uint16_t nb_rx; + uint16_t i; + uint16_t j; + + while (!force_quit) { + for (i = 0; i < nr_queues; i++) { + nb_rx = rte_eth_rx_burst(port_id, + i, mbufs, 32); + if (nb_rx) { + for (j = 0; j < nb_rx; j++) { + struct rte_mbuf *m = mbufs[j]; + + eth_hdr = rte_pktmbuf_mtod(m, + struct ether_hdr *); + print_ether_addr("src=", + ð_hdr->s_addr); + print_ether_addr(" - dst=", + ð_hdr->d_addr); + printf(" - queue=0x%x", + (unsigned int)i); + printf("\n"); + rte_pktmbuf_free(m); + } + } + } + } + /* closing and releasing resources */ + rte_flow_flush(port_id, &error); + rte_eth_dev_stop(port_id); + rte_eth_dev_close(port_id); + } + +The main work of the application is reading the packets from all +queues and printing for each packet the destination queue: + +.. code-block:: c + + while (!force_quit) { + for (i = 0; i < nr_queues; i++) { + nb_rx = rte_eth_rx_burst(port_id, i, mbufs, 32); + if (nb_rx) { + for (j = 0; j < nb_rx; j++) { + struct rte_mbuf *m = mbufs[j]; + eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *); + print_ether_addr("src=", ð_hdr->s_addr); + print_ether_addr(" - dst=", ð_hdr->d_addr); + printf(" - queue=0x%x", (unsigned int)i); + printf("\n"); + rte_pktmbuf_free(m); + } + } + } + } + + +The forwarding loop can be interrupted and the application closed using +``Ctrl-C``. Which results in closing the port and the device using +``rte_eth_dev_stop`` and ``rte_eth_dev_close`` + +The generate_ipv4_flow function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The generate_ipv4_rule function is responsible for creating the flow rule. +This function is located in the ``flow_blocks.c`` file. + +.. code-block:: c + + static struct rte_flow * + generate_ipv4_flow(uint8_t port_id, uint16_t rx_q, + uint32_t src_ip, uint32_t src_mask, + uint32_t dest_ip, uint32_t dest_mask, + struct rte_flow_error *error) + { + struct rte_flow_attr attr; + struct rte_flow_item pattern[MAX_PATTERN_NUM]; + struct rte_flow_action action[MAX_PATTERN_NUM]; + struct rte_flow *flow = NULL; + struct rte_flow_action_queue queue = { .index = rx_q }; + struct rte_flow_item_eth eth_spec; + struct rte_flow_item_eth eth_mask; + struct rte_flow_item_vlan vlan_spec; + struct rte_flow_item_vlan vlan_mask; + struct rte_flow_item_ipv4 ip_spec; + struct rte_flow_item_ipv4 ip_mask; + + memset(pattern, 0, sizeof(pattern)); + memset(action, 0, sizeof(action)); + + /* + * set the rule attribute. + * in this case only ingress packets will be checked. + */ + memset(&attr, 0, sizeof(struct rte_flow_attr)); + attr.ingress = 1; + + /* + * create the action sequence. + * one action only, move packet to queue + */ + + action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE; + action[0].conf = &queue; + action[1].type = RTE_FLOW_ACTION_TYPE_END; + + /* + * set the first level of the pattern (eth). + * since in this example we just want to get the + * ipv4 we set this level to allow all. + */ + memset(ð_spec, 0, sizeof(struct rte_flow_item_eth)); + memset(ð_mask, 0, sizeof(struct rte_flow_item_eth)); + eth_spec.type = 0; + eth_mask.type = 0; + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[0].spec = ð_spec; + pattern[0].mask = ð_mask; + + /* + * setting the second level of the pattern (vlan). + * since in this example we just want to get the + * ipv4 we also set this level to allow all. + */ + memset(&vlan_spec, 0, sizeof(struct rte_flow_item_vlan)); + memset(&vlan_mask, 0, sizeof(struct rte_flow_item_vlan)); + pattern[1].type = RTE_FLOW_ITEM_TYPE_VLAN; + pattern[1].spec = &vlan_spec; + pattern[1].mask = &vlan_mask; + + /* + * setting the third level of the pattern (ip). + * in this example this is the level we care about + * so we set it according to the parameters. + */ + memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4)); + memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4)); + ip_spec.hdr.dst_addr = htonl(dest_ip); + ip_mask.hdr.dst_addr = dest_mask; + ip_spec.hdr.src_addr = htonl(src_ip); + ip_mask.hdr.src_addr = src_mask; + pattern[2].type = RTE_FLOW_ITEM_TYPE_IPV4; + pattern[2].spec = &ip_spec; + pattern[2].mask = &ip_mask; + + /* the final level must be always type end */ + pattern[3].type = RTE_FLOW_ITEM_TYPE_END; + + int res = rte_flow_validate(port_id, &attr, pattern, action, error); + if(!res) + flow = rte_flow_create(port_id, &attr, pattern, action, error); + + return flow; + } + +The first part of the function is declaring the structures that will be used. + +.. code-block:: c + + struct rte_flow_attr attr; + struct rte_flow_item pattern[MAX_PATTERN_NUM]; + struct rte_flow_action action[MAX_PATTERN_NUM]; + struct rte_flow *flow; + struct rte_flow_error error; + struct rte_flow_action_queue queue = { .index = rx_q }; + struct rte_flow_item_eth eth_spec; + struct rte_flow_item_eth eth_mask; + struct rte_flow_item_vlan vlan_spec; + struct rte_flow_item_vlan vlan_mask; + struct rte_flow_item_ipv4 ip_spec; + struct rte_flow_item_ipv4 ip_mask; + +The following part create the flow attributes, in our case ingress. + +.. code-block:: c + + memset(&attr, 0, sizeof(struct rte_flow_attr)); + attr.ingress = 1; + +The third part defines the action to be taken when a packet matches +the rule. In this case send the packet to queue. + +.. code-block:: c + + action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE; + action[0].conf = &queue; + action[1].type = RTE_FLOW_ACTION_TYPE_END; + +The forth part is responsible for creating the pattern and is build from +number of step. In each step we build one level of the pattern starting with +the lowest one. + +Setting the first level of the pattern ETH: + +.. code-block:: c + + memset(ð_spec, 0, sizeof(struct rte_flow_item_eth)); + memset(ð_mask, 0, sizeof(struct rte_flow_item_eth)); + eth_spec.type = 0; + eth_mask.type = 0; + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[0].spec = ð_spec; + pattern[0].mask = ð_mask; + +Setting the second level of the pattern VLAN: + +.. code-block:: c + + memset(&vlan_spec, 0, sizeof(struct rte_flow_item_vlan)); + memset(&vlan_mask, 0, sizeof(struct rte_flow_item_vlan)); + pattern[1].type = RTE_FLOW_ITEM_TYPE_VLAN; + pattern[1].spec = &vlan_spec; + pattern[1].mask = &vlan_mask; + +Setting the third level ip: + +.. code-block:: c + + memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4)); + memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4)); + ip_spec.hdr.dst_addr = htonl(dest_ip); + ip_mask.hdr.dst_addr = dest_mask; + ip_spec.hdr.src_addr = htonl(src_ip); + ip_mask.hdr.src_addr = src_mask; + pattern[2].type = RTE_FLOW_ITEM_TYPE_IPV4; + pattern[2].spec = &ip_spec; + pattern[2].mask = &ip_mask; + +Closing the pattern part. + +.. code-block:: c + + pattern[3].type = RTE_FLOW_ITEM_TYPE_END; + +The last part of the function is to validate the rule and create it. + +.. code-block:: c + + int res = rte_flow_validate(port_id, &attr, pattern, action, &error); + if (!res) + flow = rte_flow_create(port_id, &attr, pattern, action, &error); + diff --git a/doc/guides/sample_app_ug/index.rst b/doc/guides/sample_app_ug/index.rst index 069d4f1..ca1f544 100644 --- a/doc/guides/sample_app_ug/index.rst +++ b/doc/guides/sample_app_ug/index.rst @@ -77,6 +77,7 @@ Sample Applications User Guides ptpclient performance_thread ipsec_secgw + flow_filtering **Figures** diff --git a/examples/Makefile b/examples/Makefile index d27eddd..761f789 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -100,5 +100,6 @@ endif endif DIRS-y += eventdev_pipeline_sw_pmd +DIRS-y += flow_filtering include $(RTE_SDK)/mk/rte.extsubdir.mk diff --git a/examples/flow_filtering/Makefile b/examples/flow_filtering/Makefile new file mode 100644 index 0000000..6e5295d --- /dev/null +++ b/examples/flow_filtering/Makefile @@ -0,0 +1,17 @@ +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 + +APP = flow + +SRCS-y := main.c + +CFLAGS += -g3 +CFLAGS += $(WERROR_FLAGS) + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/flow_filtering/flow_blocks.c b/examples/flow_filtering/flow_blocks.c new file mode 100644 index 0000000..2aeb62c --- /dev/null +++ b/examples/flow_filtering/flow_blocks.c @@ -0,0 +1,150 @@ +/*- + * BSD LICENSE + * + * Copyright 2017 Mellanox. + * + * 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 Mellanox 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. + */ + +#define MAX_PATTERN_NUM 4 + +static struct rte_flow * +generate_ipv4_flow(uint8_t port_id, uint16_t rx_q, + uint32_t src_ip, uint32_t src_mask, + uint32_t dest_ip, uint32_t dest_mask, + struct rte_flow_error *error); + + +/** + * create a flow rule that sends packets with matching src and dest ip + * to selected queue. + * + * @param port_id + * The selected port. + * @param rx_q + * The selected target queue. + * @param src_ip + * The src ip value to match the input packet. + * @param src_mask + * The mask to apply to the src ip. + * @param dest_ip + * The dest ip value to match the input packet. + * @param dest_mask + * The mask to apply to the dest ip. + * @param[out] error + * Perform verbose error reporting if not NULL. + * + * @return + * A flow if the rule could be created else return NULL. + */ +static struct rte_flow * +generate_ipv4_flow(uint8_t port_id, uint16_t rx_q, + uint32_t src_ip, uint32_t src_mask, + uint32_t dest_ip, uint32_t dest_mask, + struct rte_flow_error *error) +{ + struct rte_flow_attr attr; + struct rte_flow_item pattern[MAX_PATTERN_NUM]; + struct rte_flow_action action[MAX_PATTERN_NUM]; + struct rte_flow *flow = NULL; + struct rte_flow_action_queue queue = { .index = rx_q }; + struct rte_flow_item_eth eth_spec; + struct rte_flow_item_eth eth_mask; + struct rte_flow_item_vlan vlan_spec; + struct rte_flow_item_vlan vlan_mask; + struct rte_flow_item_ipv4 ip_spec; + struct rte_flow_item_ipv4 ip_mask; + int res; + + memset(pattern, 0, sizeof(pattern)); + memset(action, 0, sizeof(action)); + + /* + * set the rule attribute. + * in this case only ingress packets will be checked. + */ + memset(&attr, 0, sizeof(struct rte_flow_attr)); + attr.ingress = 1; + + /* + * create the action sequence. + * one action only, move packet to queue + */ + + action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE; + action[0].conf = &queue; + action[1].type = RTE_FLOW_ACTION_TYPE_END; + + /* + * set the first level of the pattern (eth). + * since in this example we just want to get the + * ipv4 we set this level to allow all. + */ + memset(ð_spec, 0, sizeof(struct rte_flow_item_eth)); + memset(ð_mask, 0, sizeof(struct rte_flow_item_eth)); + eth_spec.type = 0; + eth_mask.type = 0; + pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; + pattern[0].spec = ð_spec; + pattern[0].mask = ð_mask; + + /* + * setting the second level of the pattern (vlan). + * since in this example we just want to get the + * ipv4 we also set this level to allow all. + */ + memset(&vlan_spec, 0, sizeof(struct rte_flow_item_vlan)); + memset(&vlan_mask, 0, sizeof(struct rte_flow_item_vlan)); + pattern[1].type = RTE_FLOW_ITEM_TYPE_VLAN; + pattern[1].spec = &vlan_spec; + pattern[1].mask = &vlan_mask; + + /* + * setting the third level of the pattern (ip). + * in this example this is the level we care about + * so we set it according to the parameters. + */ + memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4)); + memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4)); + ip_spec.hdr.dst_addr = htonl(dest_ip); + ip_mask.hdr.dst_addr = dest_mask; + ip_spec.hdr.src_addr = htonl(src_ip); + ip_mask.hdr.src_addr = src_mask; + pattern[2].type = RTE_FLOW_ITEM_TYPE_IPV4; + pattern[2].spec = &ip_spec; + pattern[2].mask = &ip_mask; + + /* the final level must be always type end */ + pattern[3].type = RTE_FLOW_ITEM_TYPE_END; + + res = rte_flow_validate(port_id, &attr, pattern, action, error); + if (!res) + flow = rte_flow_create(port_id, &attr, pattern, action, error); + + return flow; +} + diff --git a/examples/flow_filtering/main.c b/examples/flow_filtering/main.c new file mode 100644 index 0000000..62e95d8 --- /dev/null +++ b/examples/flow_filtering/main.c @@ -0,0 +1,244 @@ +/*- + * BSD LICENSE + * + * Copyright 2017 Mellanox. + * + * 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 6WIND S.A. 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static volatile bool force_quit; + +static uint8_t port_id; +static uint16_t nr_queues = 5; +static uint8_t selected_queue = 1; +struct rte_mempool *mbuf_pool; +struct rte_flow *flow; + +#define SRC_IP ((0<<24) + (0<<16) + (0<<8) + 0) /* src ip = 0.0.0.0 */ +#define DEST_IP ((192<<24) + (168<<16) + (1<<8) + 1) /* dest ip = 192.168.1.1 */ +#define FULL_MASK 0xffffffff /* full mask */ +#define EMPTY_MASK 0x0 /* empty mask */ + +#include "flow_blocks.c" + +static inline void +print_ether_addr(const char *what, struct ether_addr *eth_addr) +{ + char buf[ETHER_ADDR_FMT_SIZE]; + ether_format_addr(buf, ETHER_ADDR_FMT_SIZE, eth_addr); + printf("%s%s", what, buf); +} + +static void +main_loop(void) +{ + struct rte_mbuf *mbufs[32]; + struct ether_hdr *eth_hdr; + struct rte_flow_error error; + uint16_t nb_rx; + uint16_t i; + uint16_t j; + + while (!force_quit) { + for (i = 0; i < nr_queues; i++) { + nb_rx = rte_eth_rx_burst(port_id, + i, mbufs, 32); + if (nb_rx) { + for (j = 0; j < nb_rx; j++) { + struct rte_mbuf *m = mbufs[j]; + + eth_hdr = rte_pktmbuf_mtod(m, + struct ether_hdr *); + print_ether_addr("src=", + ð_hdr->s_addr); + print_ether_addr(" - dst=", + ð_hdr->d_addr); + printf(" - queue=0x%x", + (unsigned int)i); + printf("\n"); + + rte_pktmbuf_free(m); + } + } + } + } + + /* closing and releasing resources */ + rte_flow_flush(port_id, &error); + rte_eth_dev_stop(port_id); + rte_eth_dev_close(port_id); +} + +static void +assert_link_status(void) +{ + struct rte_eth_link link; + + memset(&link, 0, sizeof(link)); + rte_eth_link_get(port_id, &link); + if (link.link_status == ETH_LINK_DOWN) + rte_exit(EXIT_FAILURE, ":: error: link is still down\n"); +} + +static void +init_port(void) +{ + int ret; + uint16_t i; + struct rte_flow_error error; + struct rte_eth_conf port_conf = { + .rxmode = { + .split_hdr_size = 0, + /**< Header Split disabled */ + .header_split = 0, + /**< IP checksum offload disabled */ + .hw_ip_checksum = 0, + /**< VLAN filtering disabled */ + .hw_vlan_filter = 0, + /**< Jumbo Frame Support disabled */ + .jumbo_frame = 0, + /**< CRC stripped by hardware */ + .hw_strip_crc = 1, + }, + }; + + printf(":: initializing port: %d\n", port_id); + ret = rte_eth_dev_configure(port_id, + nr_queues, nr_queues, &port_conf); + if (ret < 0) { + rte_exit(EXIT_FAILURE, + ":: cannot configure device: err=%d, port=%u\n", + ret, port_id); + } + + /* only set Rx queues: something we care only so far */ + for (i = 0; i < nr_queues; i++) { + ret = rte_eth_rx_queue_setup(port_id, i, 512, + rte_eth_dev_socket_id(port_id), + NULL, + mbuf_pool); + if (ret < 0) { + rte_exit(EXIT_FAILURE, + ":: Rx queue setup failed: err=%d, port=%u\n", + ret, port_id); + } + } + + /* create flow for send packet with */ + flow = generate_ipv4_flow(port_id, selected_queue, + SRC_IP, EMPTY_MASK, + DEST_IP, FULL_MASK, &error); + if (!flow) { + printf("Flow can't be created %d message: %s\n", + error.type, + error.message ? error.message : "(no stated reason)"); + rte_exit(EXIT_FAILURE, "error in creating flow"); + } + + rte_eth_promiscuous_enable(port_id); + ret = rte_eth_dev_start(port_id); + if (ret < 0) { + rte_exit(EXIT_FAILURE, + "rte_eth_dev_start:err=%d, port=%u\n", + ret, port_id); + } + + assert_link_status(); + + printf(":: initializing port: %d done\n", port_id); +} + +static void +signal_handler(int signum) +{ + if (signum == SIGINT || signum == SIGTERM) { + printf("\n\nSignal %d received, preparing to exit...\n", + signum); + force_quit = true; + } +} + +int +main(int argc, char **argv) +{ + int ret; + uint8_t nr_ports; + + ret = rte_eal_init(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, ":: invalid EAL arguments\n"); + + force_quit = false; + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + nr_ports = rte_eth_dev_count(); + if (nr_ports == 0) + rte_exit(EXIT_FAILURE, ":: no Ethernet ports found\n"); + port_id = 0; + if (nr_ports != 1) { + printf(":: warn: %d ports detected, but we use only one: port %u\n", + nr_ports, port_id); + } + mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", 4096, 128, 0, + RTE_MBUF_DEFAULT_BUF_SIZE, + rte_socket_id()); + if (mbuf_pool == NULL) + rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n"); + + init_port(); + + main_loop(); + + return 0; +}