net/hns3: support PF device with copper PHYs
[dpdk.git] / doc / guides / sample_app_ug / flow_filtering.rst
1 ..  SPDX-License-Identifier: BSD-3-Clause
2     Copyright 2017 Mellanox Technologies, Ltd
3
4 Basic RTE Flow Filtering Sample Application
5 ===========================================
6
7 The Basic RTE flow filtering sample application is a simple example of a
8 creating a RTE flow rule.
9
10 It is intended as a demonstration of the basic components RTE flow rules.
11
12
13 Compiling the Application
14 -------------------------
15
16 To compile the sample application see :doc:`compiling`.
17
18
19 Running the Application
20 -----------------------
21
22 To run the example in a ``linux`` environment:
23
24 .. code-block:: console
25
26     ./<build_dir>/examples/dpdk-flow_filtering -l 1 -n 1
27
28 Refer to *DPDK Getting Started Guide* for general information on running
29 applications and the Environment Abstraction Layer (EAL) options.
30
31
32 Explanation
33 -----------
34
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.
38
39 The following sections provide an explanation of the main components of the
40 code.
41
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*.
44
45
46 The Main Function
47 ~~~~~~~~~~~~~~~~~
48
49 The ``main()`` function located in ``main.c`` file performs the initialization
50 and runs the main loop function.
51
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:
55
56 .. code-block:: c
57
58     int ret = rte_eal_init(argc, argv);
59     if (ret < 0)
60         rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
61
62
63 The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
64 used by the application:
65
66 .. code-block:: c
67
68    mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", 4096, 128, 0,
69                                             RTE_MBUF_DEFAULT_BUF_SIZE,
70                                             rte_socket_id());
71
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*.
74
75 The ``main()`` function also initializes all the ports using the user defined
76 ``init_port()`` function which is explained in the next section:
77
78 .. code-block:: c
79
80    init_port();
81
82 Once the initialization is complete, we set the flow rule using the
83 following code:
84
85 .. code-block:: c
86
87    /* create flow for send packet with */
88    flow = generate_ipv4_flow(port_id, selected_queue,
89                                 SRC_IP, EMPTY_MASK,
90                                 DEST_IP, FULL_MASK, &error);
91    if (!flow) {
92           printf("Flow can't be created %d message: %s\n",
93                        error.type,
94                        error.message ? error.message : "(no stated reason)");
95           rte_exit(EXIT_FAILURE, "error in creating flow");
96    }
97
98 In the last part the application is ready to launch the
99 ``main_loop()`` function. Which is explained below.
100
101
102 .. code-block:: c
103
104    main_loop();
105
106 The Port Initialization  Function
107 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
108
109 The main functional part of the port initialization used in the flow filtering
110 application is shown below:
111
112 .. code-block:: c
113
114    init_port(void)
115    {
116            int ret;
117            uint16_t i;
118            struct rte_eth_conf port_conf = {
119                    .rxmode = {
120                            .split_hdr_size = 0,
121                            },
122                    .txmode = {
123                            .offloads =
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,
130                    },
131            };
132            struct rte_eth_txconf txq_conf;
133            struct rte_eth_rxconf rxq_conf;
134            struct rte_eth_dev_info dev_info;
135
136            printf(":: initializing port: %d\n", port_id);
137            ret = rte_eth_dev_configure(port_id,
138                    nr_queues, nr_queues, &port_conf);
139            if (ret < 0) {
140                    rte_exit(EXIT_FAILURE,
141                            ":: cannot configure device: err=%d, port=%u\n",
142                            ret, port_id);
143            }
144
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),
152                            &rxq_conf,
153                            mbuf_pool);
154                    if (ret < 0) {
155                             rte_exit(EXIT_FAILURE,
156                                     ":: Rx queue setup failed: err=%d, port=%u\n",
157                                     ret, port_id);
158                    }
159            }
160
161            txq_conf = dev_info.default_txconf;
162            txq_conf.offloads = port_conf.txmode.offloads;
163
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),
167                            &txq_conf);
168                    if (ret < 0) {
169                            rte_exit(EXIT_FAILURE,
170                                    ":: Tx queue setup failed: err=%d, port=%u\n",
171                                    ret, port_id);
172                    }
173           }
174
175            ret = rte_eth_promiscuous_enable(port_id);
176            if (ret != 0) {
177                    rte_exit(EXIT_FAILURE,
178                            ":: cannot enable promiscuous mode: err=%d, port=%u\n",
179                            ret, port_id);
180            }
181
182            ret = rte_eth_dev_start(port_id);
183            if (ret < 0) {
184                    rte_exit(EXIT_FAILURE,
185                            "rte_eth_dev_start:err=%d, port=%u\n",
186                            ret, port_id);
187            }
188
189            assert_link_status();
190
191            printf(":: initializing port: %d done\n", port_id);
192    }
193
194 The Ethernet port is configured with default settings using the
195 ``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct:
196
197 .. code-block:: c
198
199    struct rte_eth_conf port_conf = {
200            .rxmode = {
201                    .split_hdr_size = 0,
202                    },
203            .txmode = {
204                    .offloads =
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,
211                    },
212            };
213
214    ret = rte_eth_dev_configure(port_id, nr_queues, nr_queues, &port_conf);
215    if (ret < 0) {
216         rte_exit(EXIT_FAILURE,
217                  ":: cannot configure device: err=%d, port=%u\n",
218                  ret, port_id);
219    }
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;
223
224 For this example we are configuring number of rx and tx queues that are connected
225 to a single port.
226
227 .. code-block:: c
228
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),
232                                        &rxq_conf,
233                                        mbuf_pool);
234           if (ret < 0) {
235                   rte_exit(EXIT_FAILURE,
236                           ":: Rx queue setup failed: err=%d, port=%u\n",
237                           ret, port_id);
238           }
239    }
240
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),
244                                        &txq_conf);
245           if (ret < 0) {
246                   rte_exit(EXIT_FAILURE,
247                            ":: Tx queue setup failed: err=%d, port=%u\n",
248                            ret, port_id);
249           }
250    }
251
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:
255
256 .. code-block:: c
257
258    flow = generate_ipv4_flow(port_id, selected_queue,
259                              SRC_IP, EMPTY_MASK,
260                              DEST_IP, FULL_MASK, &error);
261
262 We are setting the RX port to promiscuous mode:
263
264 .. code-block:: c
265
266    ret = rte_eth_promiscuous_enable(port_id);
267    if (ret != 0) {
268         rte_exit(EXIT_FAILURE,
269                  ":: cannot enable promiscuous mode: err=%d, port=%u\n",
270                  ret, port_id);
271    }
272
273 The last step is to start the port.
274
275 .. code-block:: c
276
277    ret = rte_eth_dev_start(port_id);
278    if (ret < 0)  {
279         rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err%d, port=%u\n",
280                         ret, port_id);
281    }
282
283
284 The main_loop function
285 ~~~~~~~~~~~~~~~~~~~~~~
286
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:
290
291 .. code-block:: c
292
293    static void
294    main_loop(void)
295    {
296            struct rte_mbuf *mbufs[32];
297            struct rte_ether_hdr *eth_hdr;
298            uint16_t nb_rx;
299            uint16_t i;
300            uint16_t j;
301
302            while (!force_quit) {
303                    for (i = 0; i < nr_queues; i++) {
304                            nb_rx = rte_eth_rx_burst(port_id,
305                                                    i, mbufs, 32);
306                            if (nb_rx) {
307                                    for (j = 0; j < nb_rx; j++) {
308                                            struct rte_mbuf *m = mbufs[j];
309
310                                            eth_hdr = rte_pktmbuf_mtod(m,
311                                                         struct rte_ether_hdr *);
312                                            print_ether_addr("src=",
313                                                         &eth_hdr->s_addr);
314                                            print_ether_addr(" - dst=",
315                                                         &eth_hdr->d_addr);
316                                            printf(" - queue=0x%x",
317                                                            (unsigned int)i);
318                                            printf("\n");
319                                            rte_pktmbuf_free(m);
320                                    }
321                            }
322                    }
323            }
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);
328    }
329
330 The main work of the application is reading the packets from all
331 queues and printing for each packet the destination queue:
332
333 .. code-block:: c
334
335     while (!force_quit) {
336         for (i = 0; i < nr_queues; i++) {
337                    nb_rx = rte_eth_rx_burst(port_id, i, mbufs, 32);
338                 if (nb_rx) {
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=", &eth_hdr->s_addr);
343                              print_ether_addr(" - dst=", &eth_hdr->d_addr);
344                              printf(" - queue=0x%x", (unsigned int)i);
345                              printf("\n");
346                              rte_pktmbuf_free(m);
347                         }
348                 }
349            }
350     }
351
352
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``
356
357 The generate_ipv4_flow function
358 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
359
360 The generate_ipv4_flow function is responsible for creating the flow rule.
361 This function is located in the ``flow_blocks.c`` file.
362
363 .. code-block:: c
364
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)
370    {
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;
378
379            memset(pattern, 0, sizeof(pattern));
380            memset(action, 0, sizeof(action));
381
382            /*
383             * set the rule attribute.
384             * in this case only ingress packets will be checked.
385             */
386            memset(&attr, 0, sizeof(struct rte_flow_attr));
387            attr.ingress = 1;
388
389            /*
390             * create the action sequence.
391             * one action only,  move packet to queue
392             */
393            action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
394            action[0].conf = &queue;
395            action[1].type = RTE_FLOW_ACTION_TYPE_END;
396
397            /*
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.
401             */
402            pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
403
404            /*
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.
408             */
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;
418
419            /* the final level must be always type end */
420            pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
421
422            int res = rte_flow_validate(port_id, &attr, pattern, action, error);
423            if(!res)
424                flow = rte_flow_create(port_id, &attr, pattern, action, error);
425
426            return flow;
427    }
428
429 The first part of the function is declaring the structures that will be used.
430
431 .. code-block:: c
432
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;
441
442 The following part create the flow attributes, in our case ingress.
443
444 .. code-block:: c
445
446    memset(&attr, 0, sizeof(struct rte_flow_attr));
447    attr.ingress = 1;
448
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.
451
452 .. code-block:: c
453
454    action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
455    action[0].conf = &queue;
456    action[1].type = RTE_FLOW_ACTION_TYPE_END;
457
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
460 the lowest one.
461
462 Setting the first level of the pattern ETH:
463
464 .. code-block:: c
465
466    pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
467
468 Setting the second level of the pattern IP:
469
470 .. code-block:: c
471
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;
481
482 Closing the pattern part.
483
484 .. code-block:: c
485
486    pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
487
488 The last part of the function is to validate the rule and create it.
489
490 .. code-block:: c
491
492    int res = rte_flow_validate(port_id, &attr, pattern, action, &error);
493    if (!res)
494         flow = rte_flow_create(port_id, &attr, pattern, action, &error);