1 .. SPDX-License-Identifier: BSD-3-Clause
2 Copyright 2017 Mellanox Technologies, Ltd
4 Basic RTE Flow Filtering Sample Application
5 ===========================================
7 The Basic RTE flow filtering sample application is a simple example of a
8 creating a RTE flow rule.
10 It is intended as a demonstration of the basic components RTE flow rules.
13 Compiling the Application
14 -------------------------
16 To compile the application export the path to the DPDK source tree and go to
17 the example directory:
19 .. code-block:: console
21 export RTE_SDK=/path/to/rte_sdk
23 cd ${RTE_SDK}/examples/flow_filtering
25 Set the target, for example:
27 .. code-block:: console
29 export RTE_TARGET=x86_64-native-linux-gcc
31 See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
33 Build the application as follows:
35 .. code-block:: console
40 Running the Application
41 -----------------------
43 To run the example in a ``linux`` environment:
45 .. code-block:: console
47 ./build/flow -l 1 -n 1
49 Refer to *DPDK Getting Started Guide* for general information on running
50 applications and the Environment Abstraction Layer (EAL) options.
56 The example is built from 2 files,
57 ``main.c`` which holds the example logic and ``flow_blocks.c`` that holds the
58 implementation for building the flow rule.
60 The following sections provide an explanation of the main components of the
63 All DPDK library functions used in the sample code are prefixed with ``rte_``
64 and are explained in detail in the *DPDK API Documentation*.
70 The ``main()`` function located in ``main.c`` file performs the initialization
71 and runs the main loop function.
73 The first task is to initialize the Environment Abstraction Layer (EAL). The
74 ``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
75 function. The value returned is the number of parsed arguments:
79 int ret = rte_eal_init(argc, argv);
81 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
84 The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
85 used by the application:
89 mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", 4096, 128, 0,
90 RTE_MBUF_DEFAULT_BUF_SIZE,
93 Mbufs are the packet buffer structure used by DPDK. They are explained in
94 detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
96 The ``main()`` function also initializes all the ports using the user defined
97 ``init_port()`` function which is explained in the next section:
103 Once the initialization is complete, we set the flow rule using the
108 /* create flow for send packet with */
109 flow = generate_ipv4_flow(port_id, selected_queue,
111 DEST_IP, FULL_MASK, &error);
113 printf("Flow can't be created %d message: %s\n",
115 error.message ? error.message : "(no stated reason)");
116 rte_exit(EXIT_FAILURE, "error in creating flow");
119 In the last part the application is ready to launch the
120 ``main_loop()`` function. Which is explained below.
127 The Port Initialization Function
128 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
130 The main functional part of the port initialization used in the flow filtering
131 application is shown below:
139 struct rte_eth_conf port_conf = {
145 DEV_TX_OFFLOAD_VLAN_INSERT |
146 DEV_TX_OFFLOAD_IPV4_CKSUM |
147 DEV_TX_OFFLOAD_UDP_CKSUM |
148 DEV_TX_OFFLOAD_TCP_CKSUM |
149 DEV_TX_OFFLOAD_SCTP_CKSUM |
150 DEV_TX_OFFLOAD_TCP_TSO,
153 struct rte_eth_txconf txq_conf;
154 struct rte_eth_rxconf rxq_conf;
155 struct rte_eth_dev_info dev_info;
157 printf(":: initializing port: %d\n", port_id);
158 ret = rte_eth_dev_configure(port_id,
159 nr_queues, nr_queues, &port_conf);
161 rte_exit(EXIT_FAILURE,
162 ":: cannot configure device: err=%d, port=%u\n",
166 rte_eth_dev_info_get(port_id, &dev_info);
167 rxq_conf = dev_info.default_rxconf;
168 rxq_conf.offloads = port_conf.rxmode.offloads;
169 /* only set Rx queues: something we care only so far */
170 for (i = 0; i < nr_queues; i++) {
171 ret = rte_eth_rx_queue_setup(port_id, i, 512,
172 rte_eth_dev_socket_id(port_id),
176 rte_exit(EXIT_FAILURE,
177 ":: Rx queue setup failed: err=%d, port=%u\n",
182 txq_conf = dev_info.default_txconf;
183 txq_conf.offloads = port_conf.txmode.offloads;
185 for (i = 0; i < nr_queues; i++) {
186 ret = rte_eth_tx_queue_setup(port_id, i, 512,
187 rte_eth_dev_socket_id(port_id),
190 rte_exit(EXIT_FAILURE,
191 ":: Tx queue setup failed: err=%d, port=%u\n",
196 ret = rte_eth_promiscuous_enable(port_id);
198 rte_exit(EXIT_FAILURE,
199 ":: cannot enable promiscuous mode: err=%d, port=%u\n",
203 ret = rte_eth_dev_start(port_id);
205 rte_exit(EXIT_FAILURE,
206 "rte_eth_dev_start:err=%d, port=%u\n",
210 assert_link_status();
212 printf(":: initializing port: %d done\n", port_id);
215 The Ethernet port is configured with default settings using the
216 ``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct:
220 struct rte_eth_conf port_conf = {
226 DEV_TX_OFFLOAD_VLAN_INSERT |
227 DEV_TX_OFFLOAD_IPV4_CKSUM |
228 DEV_TX_OFFLOAD_UDP_CKSUM |
229 DEV_TX_OFFLOAD_TCP_CKSUM |
230 DEV_TX_OFFLOAD_SCTP_CKSUM |
231 DEV_TX_OFFLOAD_TCP_TSO,
235 ret = rte_eth_dev_configure(port_id, nr_queues, nr_queues, &port_conf);
237 rte_exit(EXIT_FAILURE,
238 ":: cannot configure device: err=%d, port=%u\n",
241 rte_eth_dev_info_get(port_id, &dev_info);
242 rxq_conf = dev_info.default_rxconf;
243 rxq_conf.offloads = port_conf.rxmode.offloads;
245 For this example we are configuring number of rx and tx queues that are connected
250 for (i = 0; i < nr_queues; i++) {
251 ret = rte_eth_rx_queue_setup(port_id, i, 512,
252 rte_eth_dev_socket_id(port_id),
256 rte_exit(EXIT_FAILURE,
257 ":: Rx queue setup failed: err=%d, port=%u\n",
262 for (i = 0; i < nr_queues; i++) {
263 ret = rte_eth_tx_queue_setup(port_id, i, 512,
264 rte_eth_dev_socket_id(port_id),
267 rte_exit(EXIT_FAILURE,
268 ":: Tx queue setup failed: err=%d, port=%u\n",
273 In the next step we create and apply the flow rule. which is to send packets
274 with destination ip equals to 192.168.1.1 to queue number 1. The detail
275 explanation of the ``generate_ipv4_flow()`` appears later in this document:
279 flow = generate_ipv4_flow(port_id, selected_queue,
281 DEST_IP, FULL_MASK, &error);
283 We are setting the RX port to promiscuous mode:
287 ret = rte_eth_promiscuous_enable(port_id);
289 rte_exit(EXIT_FAILURE,
290 ":: cannot enable promiscuous mode: err=%d, port=%u\n",
294 The last step is to start the port.
298 ret = rte_eth_dev_start(port_id);
300 rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err%d, port=%u\n",
305 The main_loop function
306 ~~~~~~~~~~~~~~~~~~~~~~
308 As we saw above the ``main()`` function calls an application function to handle
309 the main loop. For the flow filtering application the main_loop function
310 looks like the following:
317 struct rte_mbuf *mbufs[32];
318 struct rte_ether_hdr *eth_hdr;
323 while (!force_quit) {
324 for (i = 0; i < nr_queues; i++) {
325 nb_rx = rte_eth_rx_burst(port_id,
328 for (j = 0; j < nb_rx; j++) {
329 struct rte_mbuf *m = mbufs[j];
331 eth_hdr = rte_pktmbuf_mtod(m,
332 struct rte_ether_hdr *);
333 print_ether_addr("src=",
335 print_ether_addr(" - dst=",
337 printf(" - queue=0x%x",
345 /* closing and releasing resources */
346 rte_flow_flush(port_id, &error);
347 rte_eth_dev_stop(port_id);
348 rte_eth_dev_close(port_id);
351 The main work of the application is reading the packets from all
352 queues and printing for each packet the destination queue:
356 while (!force_quit) {
357 for (i = 0; i < nr_queues; i++) {
358 nb_rx = rte_eth_rx_burst(port_id, i, mbufs, 32);
360 for (j = 0; j < nb_rx; j++) {
361 struct rte_mbuf *m = mbufs[j];
362 eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
363 print_ether_addr("src=", ð_hdr->s_addr);
364 print_ether_addr(" - dst=", ð_hdr->d_addr);
365 printf(" - queue=0x%x", (unsigned int)i);
374 The forwarding loop can be interrupted and the application closed using
375 ``Ctrl-C``. Which results in closing the port and the device using
376 ``rte_eth_dev_stop`` and ``rte_eth_dev_close``
378 The generate_ipv4_flow function
379 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
381 The generate_ipv4_flow function is responsible for creating the flow rule.
382 This function is located in the ``flow_blocks.c`` file.
386 static struct rte_flow *
387 generate_ipv4_flow(uint8_t port_id, uint16_t rx_q,
388 uint32_t src_ip, uint32_t src_mask,
389 uint32_t dest_ip, uint32_t dest_mask,
390 struct rte_flow_error *error)
392 struct rte_flow_attr attr;
393 struct rte_flow_item pattern[MAX_PATTERN_NUM];
394 struct rte_flow_action action[MAX_ACTION_NUM];
395 struct rte_flow *flow = NULL;
396 struct rte_flow_action_queue queue = { .index = rx_q };
397 struct rte_flow_item_ipv4 ip_spec;
398 struct rte_flow_item_ipv4 ip_mask;
400 memset(pattern, 0, sizeof(pattern));
401 memset(action, 0, sizeof(action));
404 * set the rule attribute.
405 * in this case only ingress packets will be checked.
407 memset(&attr, 0, sizeof(struct rte_flow_attr));
411 * create the action sequence.
412 * one action only, move packet to queue
414 action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
415 action[0].conf = &queue;
416 action[1].type = RTE_FLOW_ACTION_TYPE_END;
419 * set the first level of the pattern (ETH).
420 * since in this example we just want to get the
421 * ipv4 we set this level to allow all.
423 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
426 * setting the second level of the pattern (IP).
427 * in this example this is the level we care about
428 * so we set it according to the parameters.
430 memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4));
431 memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4));
432 ip_spec.hdr.dst_addr = htonl(dest_ip);
433 ip_mask.hdr.dst_addr = dest_mask;
434 ip_spec.hdr.src_addr = htonl(src_ip);
435 ip_mask.hdr.src_addr = src_mask;
436 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
437 pattern[1].spec = &ip_spec;
438 pattern[1].mask = &ip_mask;
440 /* the final level must be always type end */
441 pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
443 int res = rte_flow_validate(port_id, &attr, pattern, action, error);
445 flow = rte_flow_create(port_id, &attr, pattern, action, error);
450 The first part of the function is declaring the structures that will be used.
454 struct rte_flow_attr attr;
455 struct rte_flow_item pattern[MAX_PATTERN_NUM];
456 struct rte_flow_action action[MAX_ACTION_NUM];
457 struct rte_flow *flow;
458 struct rte_flow_error error;
459 struct rte_flow_action_queue queue = { .index = rx_q };
460 struct rte_flow_item_ipv4 ip_spec;
461 struct rte_flow_item_ipv4 ip_mask;
463 The following part create the flow attributes, in our case ingress.
467 memset(&attr, 0, sizeof(struct rte_flow_attr));
470 The third part defines the action to be taken when a packet matches
471 the rule. In this case send the packet to queue.
475 action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
476 action[0].conf = &queue;
477 action[1].type = RTE_FLOW_ACTION_TYPE_END;
479 The fourth part is responsible for creating the pattern and is built from
480 number of steps. In each step we build one level of the pattern starting with
483 Setting the first level of the pattern ETH:
487 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
489 Setting the second level of the pattern IP:
493 memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4));
494 memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4));
495 ip_spec.hdr.dst_addr = htonl(dest_ip);
496 ip_mask.hdr.dst_addr = dest_mask;
497 ip_spec.hdr.src_addr = htonl(src_ip);
498 ip_mask.hdr.src_addr = src_mask;
499 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
500 pattern[1].spec = &ip_spec;
501 pattern[1].mask = &ip_mask;
503 Closing the pattern part.
507 pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
509 The last part of the function is to validate the rule and create it.
513 int res = rte_flow_validate(port_id, &attr, pattern, action, &error);
515 flow = rte_flow_create(port_id, &attr, pattern, action, &error);