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 sample application see :doc:`compiling`.
19 Running the Application
20 -----------------------
22 To run the example in a ``linux`` environment:
24 .. code-block:: console
26 ./<build_dir>/examples/dpdk-flow_filtering -l 1 -n 1
28 Refer to *DPDK Getting Started Guide* for general information on running
29 applications and the Environment Abstraction Layer (EAL) options.
35 The example is built from 2 files,
36 ``main.c`` which holds the example logic and ``flow_blocks.c`` that holds the
37 implementation for building the flow rule.
39 The following sections provide an explanation of the main components of the
42 All DPDK library functions used in the sample code are prefixed with ``rte_``
43 and are explained in detail in the *DPDK API Documentation*.
49 The ``main()`` function located in ``main.c`` file performs the initialization
50 and runs the main loop function.
52 The first task is to initialize the Environment Abstraction Layer (EAL). The
53 ``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
54 function. The value returned is the number of parsed arguments:
58 int ret = rte_eal_init(argc, argv);
60 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
63 The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
64 used by the application:
68 mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", 4096, 128, 0,
69 RTE_MBUF_DEFAULT_BUF_SIZE,
72 Mbufs are the packet buffer structure used by DPDK. They are explained in
73 detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
75 The ``main()`` function also initializes all the ports using the user defined
76 ``init_port()`` function which is explained in the next section:
82 Once the initialization is complete, we set the flow rule using the
87 /* create flow for send packet with */
88 flow = generate_ipv4_flow(port_id, selected_queue,
90 DEST_IP, FULL_MASK, &error);
92 printf("Flow can't be created %d message: %s\n",
94 error.message ? error.message : "(no stated reason)");
95 rte_exit(EXIT_FAILURE, "error in creating flow");
98 In the last part the application is ready to launch the
99 ``main_loop()`` function. Which is explained below.
106 The Port Initialization Function
107 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
109 The main functional part of the port initialization used in the flow filtering
110 application is shown below:
118 struct rte_eth_conf port_conf = {
124 DEV_TX_OFFLOAD_VLAN_INSERT |
125 DEV_TX_OFFLOAD_IPV4_CKSUM |
126 DEV_TX_OFFLOAD_UDP_CKSUM |
127 DEV_TX_OFFLOAD_TCP_CKSUM |
128 DEV_TX_OFFLOAD_SCTP_CKSUM |
129 DEV_TX_OFFLOAD_TCP_TSO,
132 struct rte_eth_txconf txq_conf;
133 struct rte_eth_rxconf rxq_conf;
134 struct rte_eth_dev_info dev_info;
136 printf(":: initializing port: %d\n", port_id);
137 ret = rte_eth_dev_configure(port_id,
138 nr_queues, nr_queues, &port_conf);
140 rte_exit(EXIT_FAILURE,
141 ":: cannot configure device: err=%d, port=%u\n",
145 rte_eth_dev_info_get(port_id, &dev_info);
146 rxq_conf = dev_info.default_rxconf;
147 rxq_conf.offloads = port_conf.rxmode.offloads;
148 /* only set Rx queues: something we care only so far */
149 for (i = 0; i < nr_queues; i++) {
150 ret = rte_eth_rx_queue_setup(port_id, i, 512,
151 rte_eth_dev_socket_id(port_id),
155 rte_exit(EXIT_FAILURE,
156 ":: Rx queue setup failed: err=%d, port=%u\n",
161 txq_conf = dev_info.default_txconf;
162 txq_conf.offloads = port_conf.txmode.offloads;
164 for (i = 0; i < nr_queues; i++) {
165 ret = rte_eth_tx_queue_setup(port_id, i, 512,
166 rte_eth_dev_socket_id(port_id),
169 rte_exit(EXIT_FAILURE,
170 ":: Tx queue setup failed: err=%d, port=%u\n",
175 ret = rte_eth_promiscuous_enable(port_id);
177 rte_exit(EXIT_FAILURE,
178 ":: cannot enable promiscuous mode: err=%d, port=%u\n",
182 ret = rte_eth_dev_start(port_id);
184 rte_exit(EXIT_FAILURE,
185 "rte_eth_dev_start:err=%d, port=%u\n",
189 assert_link_status();
191 printf(":: initializing port: %d done\n", port_id);
194 The Ethernet port is configured with default settings using the
195 ``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct:
199 struct rte_eth_conf port_conf = {
205 DEV_TX_OFFLOAD_VLAN_INSERT |
206 DEV_TX_OFFLOAD_IPV4_CKSUM |
207 DEV_TX_OFFLOAD_UDP_CKSUM |
208 DEV_TX_OFFLOAD_TCP_CKSUM |
209 DEV_TX_OFFLOAD_SCTP_CKSUM |
210 DEV_TX_OFFLOAD_TCP_TSO,
214 ret = rte_eth_dev_configure(port_id, nr_queues, nr_queues, &port_conf);
216 rte_exit(EXIT_FAILURE,
217 ":: cannot configure device: err=%d, port=%u\n",
220 rte_eth_dev_info_get(port_id, &dev_info);
221 rxq_conf = dev_info.default_rxconf;
222 rxq_conf.offloads = port_conf.rxmode.offloads;
224 For this example we are configuring number of rx and tx queues that are connected
229 for (i = 0; i < nr_queues; i++) {
230 ret = rte_eth_rx_queue_setup(port_id, i, 512,
231 rte_eth_dev_socket_id(port_id),
235 rte_exit(EXIT_FAILURE,
236 ":: Rx queue setup failed: err=%d, port=%u\n",
241 for (i = 0; i < nr_queues; i++) {
242 ret = rte_eth_tx_queue_setup(port_id, i, 512,
243 rte_eth_dev_socket_id(port_id),
246 rte_exit(EXIT_FAILURE,
247 ":: Tx queue setup failed: err=%d, port=%u\n",
252 In the next step we create and apply the flow rule. which is to send packets
253 with destination ip equals to 192.168.1.1 to queue number 1. The detail
254 explanation of the ``generate_ipv4_flow()`` appears later in this document:
258 flow = generate_ipv4_flow(port_id, selected_queue,
260 DEST_IP, FULL_MASK, &error);
262 We are setting the RX port to promiscuous mode:
266 ret = rte_eth_promiscuous_enable(port_id);
268 rte_exit(EXIT_FAILURE,
269 ":: cannot enable promiscuous mode: err=%d, port=%u\n",
273 The last step is to start the port.
277 ret = rte_eth_dev_start(port_id);
279 rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err%d, port=%u\n",
284 The main_loop function
285 ~~~~~~~~~~~~~~~~~~~~~~
287 As we saw above the ``main()`` function calls an application function to handle
288 the main loop. For the flow filtering application the main_loop function
289 looks like the following:
296 struct rte_mbuf *mbufs[32];
297 struct rte_ether_hdr *eth_hdr;
302 while (!force_quit) {
303 for (i = 0; i < nr_queues; i++) {
304 nb_rx = rte_eth_rx_burst(port_id,
307 for (j = 0; j < nb_rx; j++) {
308 struct rte_mbuf *m = mbufs[j];
310 eth_hdr = rte_pktmbuf_mtod(m,
311 struct rte_ether_hdr *);
312 print_ether_addr("src=",
314 print_ether_addr(" - dst=",
316 printf(" - queue=0x%x",
324 /* closing and releasing resources */
325 rte_flow_flush(port_id, &error);
326 rte_eth_dev_stop(port_id);
327 rte_eth_dev_close(port_id);
330 The main work of the application is reading the packets from all
331 queues and printing for each packet the destination queue:
335 while (!force_quit) {
336 for (i = 0; i < nr_queues; i++) {
337 nb_rx = rte_eth_rx_burst(port_id, i, mbufs, 32);
339 for (j = 0; j < nb_rx; j++) {
340 struct rte_mbuf *m = mbufs[j];
341 eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
342 print_ether_addr("src=", ð_hdr->s_addr);
343 print_ether_addr(" - dst=", ð_hdr->d_addr);
344 printf(" - queue=0x%x", (unsigned int)i);
353 The forwarding loop can be interrupted and the application closed using
354 ``Ctrl-C``. Which results in closing the port and the device using
355 ``rte_eth_dev_stop`` and ``rte_eth_dev_close``
357 The generate_ipv4_flow function
358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
360 The generate_ipv4_flow function is responsible for creating the flow rule.
361 This function is located in the ``flow_blocks.c`` file.
365 static struct rte_flow *
366 generate_ipv4_flow(uint16_t port_id, uint16_t rx_q,
367 uint32_t src_ip, uint32_t src_mask,
368 uint32_t dest_ip, uint32_t dest_mask,
369 struct rte_flow_error *error)
371 struct rte_flow_attr attr;
372 struct rte_flow_item pattern[MAX_PATTERN_NUM];
373 struct rte_flow_action action[MAX_ACTION_NUM];
374 struct rte_flow *flow = NULL;
375 struct rte_flow_action_queue queue = { .index = rx_q };
376 struct rte_flow_item_ipv4 ip_spec;
377 struct rte_flow_item_ipv4 ip_mask;
379 memset(pattern, 0, sizeof(pattern));
380 memset(action, 0, sizeof(action));
383 * set the rule attribute.
384 * in this case only ingress packets will be checked.
386 memset(&attr, 0, sizeof(struct rte_flow_attr));
390 * create the action sequence.
391 * one action only, move packet to queue
393 action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
394 action[0].conf = &queue;
395 action[1].type = RTE_FLOW_ACTION_TYPE_END;
398 * set the first level of the pattern (ETH).
399 * since in this example we just want to get the
400 * ipv4 we set this level to allow all.
402 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
405 * setting the second level of the pattern (IP).
406 * in this example this is the level we care about
407 * so we set it according to the parameters.
409 memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4));
410 memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4));
411 ip_spec.hdr.dst_addr = htonl(dest_ip);
412 ip_mask.hdr.dst_addr = dest_mask;
413 ip_spec.hdr.src_addr = htonl(src_ip);
414 ip_mask.hdr.src_addr = src_mask;
415 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
416 pattern[1].spec = &ip_spec;
417 pattern[1].mask = &ip_mask;
419 /* the final level must be always type end */
420 pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
422 int res = rte_flow_validate(port_id, &attr, pattern, action, error);
424 flow = rte_flow_create(port_id, &attr, pattern, action, error);
429 The first part of the function is declaring the structures that will be used.
433 struct rte_flow_attr attr;
434 struct rte_flow_item pattern[MAX_PATTERN_NUM];
435 struct rte_flow_action action[MAX_ACTION_NUM];
436 struct rte_flow *flow;
437 struct rte_flow_error error;
438 struct rte_flow_action_queue queue = { .index = rx_q };
439 struct rte_flow_item_ipv4 ip_spec;
440 struct rte_flow_item_ipv4 ip_mask;
442 The following part create the flow attributes, in our case ingress.
446 memset(&attr, 0, sizeof(struct rte_flow_attr));
449 The third part defines the action to be taken when a packet matches
450 the rule. In this case send the packet to queue.
454 action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
455 action[0].conf = &queue;
456 action[1].type = RTE_FLOW_ACTION_TYPE_END;
458 The fourth part is responsible for creating the pattern and is built from
459 number of steps. In each step we build one level of the pattern starting with
462 Setting the first level of the pattern ETH:
466 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
468 Setting the second level of the pattern IP:
472 memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4));
473 memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4));
474 ip_spec.hdr.dst_addr = htonl(dest_ip);
475 ip_mask.hdr.dst_addr = dest_mask;
476 ip_spec.hdr.src_addr = htonl(src_ip);
477 ip_mask.hdr.src_addr = src_mask;
478 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
479 pattern[1].spec = &ip_spec;
480 pattern[1].mask = &ip_mask;
482 Closing the pattern part.
486 pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
488 The last part of the function is to validate the rule and create it.
492 int res = rte_flow_validate(port_id, &attr, pattern, action, &error);
494 flow = rte_flow_create(port_id, &attr, pattern, action, &error);