2 Copyright(c) 2017 Intel Corporation. All rights reserved.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in
13 the documentation and/or other materials provided with the
15 * Neither the name of Intel Corporation nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 Flow Classify Sample Application
32 ================================
34 The Flow Classify sample application is based on the simple *skeleton* example
35 of a forwarding application.
37 It is intended as a demonstration of the basic components of a DPDK forwarding
38 application which uses the Flow Classify library API's.
41 :doc:`../prog_guide/flow_classify_lib`
44 Compiling the Application
45 -------------------------
47 To compile the sample application see :doc:`compiling`.
49 The application is located in the ``flow_classify`` sub-directory.
51 Running the Application
52 -----------------------
54 To run the example in a ``linuxapp`` environment:
56 .. code-block:: console
58 cd ~/dpdk/examples/flow_classify
59 ./build/flow_classify -c 4 -n 4 -- --rule_ipv4="../ipv4_rules_file.txt"
61 Please refer to the *DPDK Getting Started Guide*, section
62 :doc:`../linux_gsg/build_sample_apps`
63 for general information on running applications and the Environment Abstraction
67 Sample ipv4_rules_file.txt
68 --------------------------
70 .. code-block:: console
73 #src_ip/masklen dst_ip/masklen src_port : mask dst_port : mask proto/mask priority
75 2.2.2.3/24 2.2.2.7/24 32 : 0xffff 33 : 0xffff 17/0xff 0
76 9.9.9.3/24 9.9.9.7/24 32 : 0xffff 33 : 0xffff 17/0xff 1
77 9.9.9.3/24 9.9.9.7/24 32 : 0xffff 33 : 0xffff 6/0xff 2
78 9.9.8.3/24 9.9.8.7/24 32 : 0xffff 33 : 0xffff 6/0xff 3
79 6.7.8.9/24 2.3.4.5/24 32 : 0x0000 33 : 0x0000 132/0xff 4
84 The following sections provide an explanation of the main components of the
87 All DPDK library functions used in the sample code are prefixed with ``rte_``
88 and are explained in detail in the *DPDK API Documentation*.
90 ACL field definitions for the IPv4 5 tuple rule
91 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
93 The following field definitions are used when creating the ACL table during
94 initialisation of the ``Flow Classify`` application..
111 SRCP_DESTP_INPUT_IPV4
114 static struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
115 /* first input field - always one byte long. */
117 .type = RTE_ACL_FIELD_TYPE_BITMASK,
118 .size = sizeof(uint8_t),
119 .field_index = PROTO_FIELD_IPV4,
120 .input_index = PROTO_INPUT_IPV4,
121 .offset = sizeof(struct ether_hdr) +
122 offsetof(struct ipv4_hdr, next_proto_id),
124 /* next input field (IPv4 source address) - 4 consecutive bytes. */
126 /* rte_flow uses a bit mask for IPv4 addresses */
127 .type = RTE_ACL_FIELD_TYPE_BITMASK,
128 .size = sizeof(uint32_t),
129 .field_index = SRC_FIELD_IPV4,
130 .input_index = SRC_INPUT_IPV4,
131 .offset = sizeof(struct ether_hdr) +
132 offsetof(struct ipv4_hdr, src_addr),
134 /* next input field (IPv4 destination address) - 4 consecutive bytes. */
136 /* rte_flow uses a bit mask for IPv4 addresses */
137 .type = RTE_ACL_FIELD_TYPE_BITMASK,
138 .size = sizeof(uint32_t),
139 .field_index = DST_FIELD_IPV4,
140 .input_index = DST_INPUT_IPV4,
141 .offset = sizeof(struct ether_hdr) +
142 offsetof(struct ipv4_hdr, dst_addr),
145 * Next 2 fields (src & dst ports) form 4 consecutive bytes.
146 * They share the same input index.
149 /* rte_flow uses a bit mask for protocol ports */
150 .type = RTE_ACL_FIELD_TYPE_BITMASK,
151 .size = sizeof(uint16_t),
152 .field_index = SRCP_FIELD_IPV4,
153 .input_index = SRCP_DESTP_INPUT_IPV4,
154 .offset = sizeof(struct ether_hdr) +
155 sizeof(struct ipv4_hdr) +
156 offsetof(struct tcp_hdr, src_port),
159 /* rte_flow uses a bit mask for protocol ports */
160 .type = RTE_ACL_FIELD_TYPE_BITMASK,
161 .size = sizeof(uint16_t),
162 .field_index = DSTP_FIELD_IPV4,
163 .input_index = SRCP_DESTP_INPUT_IPV4,
164 .offset = sizeof(struct ether_hdr) +
165 sizeof(struct ipv4_hdr) +
166 offsetof(struct tcp_hdr, dst_port),
173 The ``main()`` function performs the initialization and calls the execution
174 threads for each lcore.
176 The first task is to initialize the Environment Abstraction Layer (EAL).
177 The ``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
178 function. The value returned is the number of parsed arguments:
182 int ret = rte_eal_init(argc, argv);
184 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
186 It then parses the flow_classify application arguments
190 ret = parse_args(argc, argv);
192 rte_exit(EXIT_FAILURE, "Invalid flow_classify parameters\n");
194 The ``main()`` function also allocates a mempool to hold the mbufs
195 (Message Buffers) used by the application:
199 mbuf_pool = rte_mempool_create("MBUF_POOL",
200 NUM_MBUFS * nb_ports,
203 sizeof(struct rte_pktmbuf_pool_private),
204 rte_pktmbuf_pool_init, NULL,
205 rte_pktmbuf_init, NULL,
209 mbufs are the packet buffer structure used by DPDK. They are explained in
210 detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
212 The ``main()`` function also initializes all the ports using the user defined
213 ``port_init()`` function which is explained in the next section:
217 for (portid = 0; portid < nb_ports; portid++) {
218 if (port_init(portid, mbuf_pool) != 0) {
219 rte_exit(EXIT_FAILURE,
220 "Cannot init port %" PRIu8 "\n", portid);
224 The ``main()`` function creates the ``flow classifier object`` and adds an ``ACL
225 table`` to the flow classifier.
229 struct flow_classifier {
230 struct rte_flow_classifier *cls;
231 uint32_t table_id[RTE_FLOW_CLASSIFY_TABLE_MAX];
234 struct flow_classifier_acl {
235 struct flow_classifier cls;
236 } __rte_cache_aligned;
238 /* Memory allocation */
239 size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct flow_classifier_acl));
240 cls_app = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
242 rte_exit(EXIT_FAILURE, "Cannot allocate classifier memory\n");
244 cls_params.name = "flow_classifier";
245 cls_params.socket_id = socket_id;
246 cls_params.type = RTE_FLOW_CLASSIFY_TABLE_TYPE_ACL;
248 cls_app->cls = rte_flow_classifier_create(&cls_params);
249 if (cls_app->cls == NULL) {
251 rte_exit(EXIT_FAILURE, "Cannot create classifier\n");
254 /* initialise ACL table params */
255 table_acl_params.name = "table_acl_ipv4_5tuple";
256 table_acl_params.n_rule_fields = RTE_DIM(ipv4_defs);
257 table_acl_params.n_rules = FLOW_CLASSIFY_MAX_RULE_NUM;
258 memcpy(table_acl_params.field_format, ipv4_defs, sizeof(ipv4_defs));
260 /* initialise table create params */
261 cls_table_params.ops = &rte_table_acl_ops,
262 cls_table_params.arg_create = &table_acl_params,
263 cls_table_params.table_metadata_size = 0;
265 ret = rte_flow_classify_table_create(cls_app->cls, &cls_table_params,
268 rte_flow_classifier_free(cls_app->cls);
270 rte_exit(EXIT_FAILURE, "Failed to create classifier table\n");
273 It then reads the ipv4_rules_file.txt file and initialises the parameters for
274 the ``rte_flow_classify_table_entry_add`` API.
275 This API adds a rule to the ACL table.
279 if (add_rules(parm_config.rule_ipv4_name)) {
280 rte_flow_classifier_free(cls_app->cls);
282 rte_exit(EXIT_FAILURE, "Failed to add rules\n");
285 Once the initialization is complete, the application is ready to launch a
286 function on an lcore. In this example ``lcore_main()`` is called on a single
293 The ``lcore_main()`` function is explained below.
295 The Port Initialization Function
296 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
298 The main functional part of the port initialization used in the Basic
299 Forwarding application is shown below:
304 port_init(uint8_t port, struct rte_mempool *mbuf_pool)
306 struct rte_eth_conf port_conf = port_conf_default;
307 const uint16_t rx_rings = 1, tx_rings = 1;
308 struct ether_addr addr;
312 if (port >= rte_eth_dev_count())
315 /* Configure the Ethernet device. */
316 retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
320 /* Allocate and set up 1 RX queue per Ethernet port. */
321 for (q = 0; q < rx_rings; q++) {
322 retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
323 rte_eth_dev_socket_id(port), NULL, mbuf_pool);
328 /* Allocate and set up 1 TX queue per Ethernet port. */
329 for (q = 0; q < tx_rings; q++) {
330 retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
331 rte_eth_dev_socket_id(port), NULL);
336 /* Start the Ethernet port. */
337 retval = rte_eth_dev_start(port);
341 /* Display the port MAC address. */
342 rte_eth_macaddr_get(port, &addr);
343 printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
344 " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
346 addr.addr_bytes[0], addr.addr_bytes[1],
347 addr.addr_bytes[2], addr.addr_bytes[3],
348 addr.addr_bytes[4], addr.addr_bytes[5]);
350 /* Enable RX in promiscuous mode for the Ethernet device. */
351 rte_eth_promiscuous_enable(port);
356 The Ethernet ports are configured with default settings using the
357 ``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct.
361 static const struct rte_eth_conf port_conf_default = {
362 .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
365 For this example the ports are set up with 1 RX and 1 TX queue using the
366 ``rte_eth_rx_queue_setup()`` and ``rte_eth_tx_queue_setup()`` functions.
368 The Ethernet port is then started:
372 retval = rte_eth_dev_start(port);
375 Finally the RX port is set in promiscuous mode:
379 rte_eth_promiscuous_enable(port);
381 The Add Rules function
382 ~~~~~~~~~~~~~~~~~~~~~~
384 The ``add_rules`` function reads the ``ipv4_rules_file.txt`` file and calls the
385 ``add_classify_rule`` function which calls the
386 ``rte_flow_classify_table_entry_add`` API.
391 add_rules(const char *rule_path)
396 unsigned int total_num = 0;
397 struct rte_eth_ntuple_filter ntuple_filter;
399 fh = fopen(rule_path, "rb");
401 rte_exit(EXIT_FAILURE, "%s: Open %s failed\n", __func__,
404 fseek(fh, 0, SEEK_SET);
407 while (fgets(buff, LINE_MAX, fh) != NULL) {
410 if (is_bypass_line(buff))
413 if (total_num >= FLOW_CLASSIFY_MAX_RULE_NUM - 1) {
414 printf("\nINFO: classify rule capacity %d reached\n",
419 if (parse_ipv4_5tuple_rule(buff, &ntuple_filter) != 0)
420 rte_exit(EXIT_FAILURE,
421 "%s Line %u: parse rules error\n",
424 if (add_classify_rule(&ntuple_filter) != 0)
425 rte_exit(EXIT_FAILURE, "add rule error\n");
435 The Lcore Main function
436 ~~~~~~~~~~~~~~~~~~~~~~~
438 As we saw above the ``main()`` function calls an application function on the
440 The ``lcore_main`` function calls the ``rte_flow_classifier_query`` API.
441 For the Basic Forwarding application the ``lcore_main`` function looks like the
446 /* flow classify data */
447 static int num_classify_rules;
448 static struct rte_flow_classify_rule *rules[MAX_NUM_CLASSIFY];
449 static struct rte_flow_classify_ipv4_5tuple_stats ntuple_stats;
450 static struct rte_flow_classify_stats classify_stats = {
451 .stats = (void *)&ntuple_stats
454 static __attribute__((noreturn)) void
457 const uint8_t nb_ports = rte_eth_dev_count();
461 * Check that the port is on the same NUMA node as the polling thread
462 * for best performance.
464 for (port = 0; port < nb_ports; port++)
465 if (rte_eth_dev_socket_id(port) > 0 &&
466 rte_eth_dev_socket_id(port) != (int)rte_socket_id()) {
468 printf("WARNING: port %u is on remote NUMA node\n",
470 printf("to polling thread.\n");
471 printf("Performance will not be optimal.\n");
473 printf("\nCore %u forwarding packets. \n",
475 printf("[Ctrl+C to quit]\n
478 /* Run until the application is quit or killed. */
481 * Receive packets on a port and forward them on the paired
482 * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
484 for (port = 0; port < nb_ports; port++) {
486 /* Get burst of RX packets, from first port of pair. */
487 struct rte_mbuf *bufs[BURST_SIZE];
488 const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
491 if (unlikely(nb_rx == 0))
494 for (i = 0; i < MAX_NUM_CLASSIFY; i++) {
496 ret = rte_flow_classifier_query(
498 cls_app->table_id[0],
499 bufs, nb_rx, rules[i],
503 "rule [%d] query failed ret [%d]\n\n",
507 "rule[%d] count=%"PRIu64"\n",
508 i, ntuple_stats.counter1);
510 printf("proto = %d\n",
511 ntuple_stats.ipv4_5tuple.proto);
516 /* Send burst of TX packets, to second port of pair. */
517 const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
520 /* Free any unsent packets. */
521 if (unlikely(nb_tx < nb_rx)) {
523 for (buf = nb_tx; buf < nb_rx; buf++)
524 rte_pktmbuf_free(bufs[buf]);
530 The main work of the application is done within the loop:
535 for (port = 0; port < nb_ports; port++) {
537 /* Get burst of RX packets, from first port of pair. */
538 struct rte_mbuf *bufs[BURST_SIZE];
539 const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
542 if (unlikely(nb_rx == 0))
545 /* Send burst of TX packets, to second port of pair. */
546 const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
549 /* Free any unsent packets. */
550 if (unlikely(nb_tx < nb_rx)) {
552 for (buf = nb_tx; buf < nb_rx; buf++)
553 rte_pktmbuf_free(bufs[buf]);
558 Packets are received in bursts on the RX ports and transmitted in bursts on
559 the TX ports. The ports are grouped in pairs with a simple mapping scheme
560 using the an XOR on the port number::
570 The ``rte_eth_tx_burst()`` function frees the memory buffers of packets that
571 are transmitted. If packets fail to transmit, ``(nb_tx < nb_rx)``, then they
572 must be freed explicitly using ``rte_pktmbuf_free()``.
574 The forwarding loop can be interrupted and the application closed using