]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/dpdk/doc/guides/sample_app_ug/flow_filtering.rst
import 15.2.0 Octopus source
[ceph.git] / ceph / src / seastar / dpdk / 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 application export the path to the DPDK source tree and go to
17 the example directory:
18
19 .. code-block:: console
20
21 export RTE_SDK=/path/to/rte_sdk
22
23 cd ${RTE_SDK}/examples/flow_filtering
24
25 Set the target, for example:
26
27 .. code-block:: console
28
29 export RTE_TARGET=x86_64-native-linux-gcc
30
31 See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
32
33 Build the application as follows:
34
35 .. code-block:: console
36
37 make
38
39
40 Running the Application
41 -----------------------
42
43 To run the example in a ``linux`` environment:
44
45 .. code-block:: console
46
47 ./build/flow -l 1 -n 1
48
49 Refer to *DPDK Getting Started Guide* for general information on running
50 applications and the Environment Abstraction Layer (EAL) options.
51
52
53 Explanation
54 -----------
55
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.
59
60 The following sections provide an explanation of the main components of the
61 code.
62
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*.
65
66
67 The Main Function
68 ~~~~~~~~~~~~~~~~~
69
70 The ``main()`` function located in ``main.c`` file performs the initialization
71 and runs the main loop function.
72
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:
76
77 .. code-block:: c
78
79 int ret = rte_eal_init(argc, argv);
80 if (ret < 0)
81 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
82
83
84 The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
85 used by the application:
86
87 .. code-block:: c
88
89 mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", 4096, 128, 0,
90 RTE_MBUF_DEFAULT_BUF_SIZE,
91 rte_socket_id());
92
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*.
95
96 The ``main()`` function also initializes all the ports using the user defined
97 ``init_port()`` function which is explained in the next section:
98
99 .. code-block:: c
100
101 init_port();
102
103 Once the initialization is complete, we set the flow rule using the
104 following code:
105
106 .. code-block:: c
107
108 /* create flow for send packet with */
109 flow = generate_ipv4_flow(port_id, selected_queue,
110 SRC_IP, EMPTY_MASK,
111 DEST_IP, FULL_MASK, &error);
112 if (!flow) {
113 printf("Flow can't be created %d message: %s\n",
114 error.type,
115 error.message ? error.message : "(no stated reason)");
116 rte_exit(EXIT_FAILURE, "error in creating flow");
117 }
118
119 In the last part the application is ready to launch the
120 ``main_loop()`` function. Which is explained below.
121
122
123 .. code-block:: c
124
125 main_loop();
126
127 The Port Initialization Function
128 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
129
130 The main functional part of the port initialization used in the flow filtering
131 application is shown below:
132
133 .. code-block:: c
134
135 init_port(void)
136 {
137 int ret;
138 uint16_t i;
139 struct rte_eth_conf port_conf = {
140 .rxmode = {
141 .split_hdr_size = 0,
142 },
143 .txmode = {
144 .offloads =
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,
151 },
152 };
153 struct rte_eth_txconf txq_conf;
154 struct rte_eth_rxconf rxq_conf;
155 struct rte_eth_dev_info dev_info;
156
157 printf(":: initializing port: %d\n", port_id);
158 ret = rte_eth_dev_configure(port_id,
159 nr_queues, nr_queues, &port_conf);
160 if (ret < 0) {
161 rte_exit(EXIT_FAILURE,
162 ":: cannot configure device: err=%d, port=%u\n",
163 ret, port_id);
164 }
165
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),
173 &rxq_conf,
174 mbuf_pool);
175 if (ret < 0) {
176 rte_exit(EXIT_FAILURE,
177 ":: Rx queue setup failed: err=%d, port=%u\n",
178 ret, port_id);
179 }
180 }
181
182 txq_conf = dev_info.default_txconf;
183 txq_conf.offloads = port_conf.txmode.offloads;
184
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),
188 &txq_conf);
189 if (ret < 0) {
190 rte_exit(EXIT_FAILURE,
191 ":: Tx queue setup failed: err=%d, port=%u\n",
192 ret, port_id);
193 }
194 }
195
196 rte_eth_promiscuous_enable(port_id);
197 ret = rte_eth_dev_start(port_id);
198 if (ret < 0) {
199 rte_exit(EXIT_FAILURE,
200 "rte_eth_dev_start:err=%d, port=%u\n",
201 ret, port_id);
202 }
203
204 assert_link_status();
205
206 printf(":: initializing port: %d done\n", port_id);
207 }
208
209 The Ethernet port is configured with default settings using the
210 ``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct:
211
212 .. code-block:: c
213
214 struct rte_eth_conf port_conf = {
215 .rxmode = {
216 .split_hdr_size = 0,
217 },
218 .txmode = {
219 .offloads =
220 DEV_TX_OFFLOAD_VLAN_INSERT |
221 DEV_TX_OFFLOAD_IPV4_CKSUM |
222 DEV_TX_OFFLOAD_UDP_CKSUM |
223 DEV_TX_OFFLOAD_TCP_CKSUM |
224 DEV_TX_OFFLOAD_SCTP_CKSUM |
225 DEV_TX_OFFLOAD_TCP_TSO,
226 },
227 };
228
229 ret = rte_eth_dev_configure(port_id, nr_queues, nr_queues, &port_conf);
230 if (ret < 0) {
231 rte_exit(EXIT_FAILURE,
232 ":: cannot configure device: err=%d, port=%u\n",
233 ret, port_id);
234 }
235 rte_eth_dev_info_get(port_id, &dev_info);
236 rxq_conf = dev_info.default_rxconf;
237 rxq_conf.offloads = port_conf.rxmode.offloads;
238
239 For this example we are configuring number of rx and tx queues that are connected
240 to a single port.
241
242 .. code-block:: c
243
244 for (i = 0; i < nr_queues; i++) {
245 ret = rte_eth_rx_queue_setup(port_id, i, 512,
246 rte_eth_dev_socket_id(port_id),
247 &rxq_conf,
248 mbuf_pool);
249 if (ret < 0) {
250 rte_exit(EXIT_FAILURE,
251 ":: Rx queue setup failed: err=%d, port=%u\n",
252 ret, port_id);
253 }
254 }
255
256 for (i = 0; i < nr_queues; i++) {
257 ret = rte_eth_tx_queue_setup(port_id, i, 512,
258 rte_eth_dev_socket_id(port_id),
259 &txq_conf);
260 if (ret < 0) {
261 rte_exit(EXIT_FAILURE,
262 ":: Tx queue setup failed: err=%d, port=%u\n",
263 ret, port_id);
264 }
265 }
266
267 In the next step we create and apply the flow rule. which is to send packets
268 with destination ip equals to 192.168.1.1 to queue number 1. The detail
269 explanation of the ``generate_ipv4_flow()`` appears later in this document:
270
271 .. code-block:: c
272
273 flow = generate_ipv4_flow(port_id, selected_queue,
274 SRC_IP, EMPTY_MASK,
275 DEST_IP, FULL_MASK, &error);
276
277 We are setting the RX port to promiscuous mode:
278
279 .. code-block:: c
280
281 rte_eth_promiscuous_enable(port_id);
282
283 The last step is to start the port.
284
285 .. code-block:: c
286
287 ret = rte_eth_dev_start(port_id);
288 if (ret < 0) {
289 rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err%d, port=%u\n",
290 ret, port_id);
291 }
292
293
294 The main_loop function
295 ~~~~~~~~~~~~~~~~~~~~~~
296
297 As we saw above the ``main()`` function calls an application function to handle
298 the main loop. For the flow filtering application the main_loop function
299 looks like the following:
300
301 .. code-block:: c
302
303 static void
304 main_loop(void)
305 {
306 struct rte_mbuf *mbufs[32];
307 struct ether_hdr *eth_hdr;
308 uint16_t nb_rx;
309 uint16_t i;
310 uint16_t j;
311
312 while (!force_quit) {
313 for (i = 0; i < nr_queues; i++) {
314 nb_rx = rte_eth_rx_burst(port_id,
315 i, mbufs, 32);
316 if (nb_rx) {
317 for (j = 0; j < nb_rx; j++) {
318 struct rte_mbuf *m = mbufs[j];
319
320 eth_hdr = rte_pktmbuf_mtod(m,
321 struct ether_hdr *);
322 print_ether_addr("src=",
323 &eth_hdr->s_addr);
324 print_ether_addr(" - dst=",
325 &eth_hdr->d_addr);
326 printf(" - queue=0x%x",
327 (unsigned int)i);
328 printf("\n");
329 rte_pktmbuf_free(m);
330 }
331 }
332 }
333 }
334 /* closing and releasing resources */
335 rte_flow_flush(port_id, &error);
336 rte_eth_dev_stop(port_id);
337 rte_eth_dev_close(port_id);
338 }
339
340 The main work of the application is reading the packets from all
341 queues and printing for each packet the destination queue:
342
343 .. code-block:: c
344
345 while (!force_quit) {
346 for (i = 0; i < nr_queues; i++) {
347 nb_rx = rte_eth_rx_burst(port_id, i, mbufs, 32);
348 if (nb_rx) {
349 for (j = 0; j < nb_rx; j++) {
350 struct rte_mbuf *m = mbufs[j];
351 eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
352 print_ether_addr("src=", &eth_hdr->s_addr);
353 print_ether_addr(" - dst=", &eth_hdr->d_addr);
354 printf(" - queue=0x%x", (unsigned int)i);
355 printf("\n");
356 rte_pktmbuf_free(m);
357 }
358 }
359 }
360 }
361
362
363 The forwarding loop can be interrupted and the application closed using
364 ``Ctrl-C``. Which results in closing the port and the device using
365 ``rte_eth_dev_stop`` and ``rte_eth_dev_close``
366
367 The generate_ipv4_flow function
368 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
369
370 The generate_ipv4_flow function is responsible for creating the flow rule.
371 This function is located in the ``flow_blocks.c`` file.
372
373 .. code-block:: c
374
375 static struct rte_flow *
376 generate_ipv4_flow(uint8_t port_id, uint16_t rx_q,
377 uint32_t src_ip, uint32_t src_mask,
378 uint32_t dest_ip, uint32_t dest_mask,
379 struct rte_flow_error *error)
380 {
381 struct rte_flow_attr attr;
382 struct rte_flow_item pattern[MAX_PATTERN_NUM];
383 struct rte_flow_action action[MAX_ACTION_NUM];
384 struct rte_flow *flow = NULL;
385 struct rte_flow_action_queue queue = { .index = rx_q };
386 struct rte_flow_item_ipv4 ip_spec;
387 struct rte_flow_item_ipv4 ip_mask;
388
389 memset(pattern, 0, sizeof(pattern));
390 memset(action, 0, sizeof(action));
391
392 /*
393 * set the rule attribute.
394 * in this case only ingress packets will be checked.
395 */
396 memset(&attr, 0, sizeof(struct rte_flow_attr));
397 attr.ingress = 1;
398
399 /*
400 * create the action sequence.
401 * one action only, move packet to queue
402 */
403 action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
404 action[0].conf = &queue;
405 action[1].type = RTE_FLOW_ACTION_TYPE_END;
406
407 /*
408 * set the first level of the pattern (ETH).
409 * since in this example we just want to get the
410 * ipv4 we set this level to allow all.
411 */
412 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
413
414 /*
415 * setting the second level of the pattern (IP).
416 * in this example this is the level we care about
417 * so we set it according to the parameters.
418 */
419 memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4));
420 memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4));
421 ip_spec.hdr.dst_addr = htonl(dest_ip);
422 ip_mask.hdr.dst_addr = dest_mask;
423 ip_spec.hdr.src_addr = htonl(src_ip);
424 ip_mask.hdr.src_addr = src_mask;
425 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
426 pattern[1].spec = &ip_spec;
427 pattern[1].mask = &ip_mask;
428
429 /* the final level must be always type end */
430 pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
431
432 int res = rte_flow_validate(port_id, &attr, pattern, action, error);
433 if(!res)
434 flow = rte_flow_create(port_id, &attr, pattern, action, error);
435
436 return flow;
437 }
438
439 The first part of the function is declaring the structures that will be used.
440
441 .. code-block:: c
442
443 struct rte_flow_attr attr;
444 struct rte_flow_item pattern[MAX_PATTERN_NUM];
445 struct rte_flow_action action[MAX_ACTION_NUM];
446 struct rte_flow *flow;
447 struct rte_flow_error error;
448 struct rte_flow_action_queue queue = { .index = rx_q };
449 struct rte_flow_item_ipv4 ip_spec;
450 struct rte_flow_item_ipv4 ip_mask;
451
452 The following part create the flow attributes, in our case ingress.
453
454 .. code-block:: c
455
456 memset(&attr, 0, sizeof(struct rte_flow_attr));
457 attr.ingress = 1;
458
459 The third part defines the action to be taken when a packet matches
460 the rule. In this case send the packet to queue.
461
462 .. code-block:: c
463
464 action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE;
465 action[0].conf = &queue;
466 action[1].type = RTE_FLOW_ACTION_TYPE_END;
467
468 The fourth part is responsible for creating the pattern and is built from
469 number of steps. In each step we build one level of the pattern starting with
470 the lowest one.
471
472 Setting the first level of the pattern ETH:
473
474 .. code-block:: c
475
476 pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
477
478 Setting the second level of the pattern IP:
479
480 .. code-block:: c
481
482 memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4));
483 memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4));
484 ip_spec.hdr.dst_addr = htonl(dest_ip);
485 ip_mask.hdr.dst_addr = dest_mask;
486 ip_spec.hdr.src_addr = htonl(src_ip);
487 ip_mask.hdr.src_addr = src_mask;
488 pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
489 pattern[1].spec = &ip_spec;
490 pattern[1].mask = &ip_mask;
491
492 Closing the pattern part.
493
494 .. code-block:: c
495
496 pattern[2].type = RTE_FLOW_ITEM_TYPE_END;
497
498 The last part of the function is to validate the rule and create it.
499
500 .. code-block:: c
501
502 int res = rte_flow_validate(port_id, &attr, pattern, action, &error);
503 if (!res)
504 flow = rte_flow_create(port_id, &attr, pattern, action, &error);
505