xref: /dpdk/examples/flow_filtering/main.c (revision 16158f34900075f2f30b879bf3708e54e07455f4)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2017 Mellanox Technologies, Ltd
3  */
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdint.h>
9 #include <inttypes.h>
10 #include <sys/types.h>
11 #include <sys/queue.h>
12 #include <setjmp.h>
13 #include <stdarg.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <getopt.h>
17 #include <signal.h>
18 #include <stdbool.h>
19 
20 #include <rte_eal.h>
21 #include <rte_common.h>
22 #include <rte_malloc.h>
23 #include <rte_ether.h>
24 #include <rte_ethdev.h>
25 #include <rte_mempool.h>
26 #include <rte_mbuf.h>
27 #include <rte_net.h>
28 #include <rte_flow.h>
29 #include <rte_cycles.h>
30 #include <rte_argparse.h>
31 
32 #include "common.h"
33 
34 /* Template API enabled by default. */
35 static int use_template_api = 1;
36 
37 static volatile bool force_quit;
38 static uint16_t port_id;
39 static uint16_t nr_queues = 5;
40 struct rte_mempool *mbuf_pool;
41 struct rte_flow *flow;
42 
43 #define MAX_QUEUE_SIZE 256
44 
45 static inline void
46 print_ether_addr(const char *what, struct rte_ether_addr *eth_addr)
47 {
48 	char buf[RTE_ETHER_ADDR_FMT_SIZE];
49 	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr);
50 	printf("%s%s", what, buf);
51 }
52 
53 static int
54 main_loop(void)
55 {
56 	struct rte_mbuf *mbufs[32];
57 	struct rte_ether_hdr *eth_hdr;
58 	struct rte_flow_error error;
59 	uint16_t nb_rx;
60 	uint16_t i;
61 	uint16_t j;
62 	int ret;
63 
64 	/* Reading the packets from all queues. */
65 	while (!force_quit) {
66 		for (i = 0; i < nr_queues; i++) {
67 			nb_rx = rte_eth_rx_burst(port_id,
68 						i, mbufs, 32);
69 			if (nb_rx) {
70 				for (j = 0; j < nb_rx; j++) {
71 					struct rte_mbuf *m = mbufs[j];
72 
73 					eth_hdr = rte_pktmbuf_mtod(m,
74 							struct rte_ether_hdr *);
75 					print_ether_addr("src=",
76 							&eth_hdr->src_addr);
77 					print_ether_addr(" - dst=",
78 							&eth_hdr->dst_addr);
79 					printf(" - queue=0x%x",
80 							(unsigned int)i);
81 					printf("\n");
82 
83 					rte_pktmbuf_free(m);
84 				}
85 			}
86 		}
87 	}
88 
89 	/* closing and releasing resources */
90 	rte_flow_flush(port_id, &error);
91 	ret = rte_eth_dev_stop(port_id);
92 	if (ret < 0)
93 		printf("Failed to stop port %u: %s",
94 			   port_id, rte_strerror(-ret));
95 	rte_eth_dev_close(port_id);
96 	return ret;
97 }
98 
99 #define CHECK_INTERVAL 1000  /* 100ms */
100 #define MAX_REPEAT_TIMES 90  /* 9s (90 * 100ms) in total */
101 
102 static void
103 assert_link_status(void)
104 {
105 	struct rte_eth_link link;
106 	uint8_t rep_cnt = MAX_REPEAT_TIMES;
107 	int link_get_err = -EINVAL;
108 
109 	memset(&link, 0, sizeof(link));
110 	do {
111 		link_get_err = rte_eth_link_get(port_id, &link);
112 		if (link_get_err == 0 && link.link_status == RTE_ETH_LINK_UP)
113 			break;
114 		rte_delay_ms(CHECK_INTERVAL);
115 	} while (--rep_cnt);
116 
117 	if (link_get_err < 0)
118 		rte_exit(EXIT_FAILURE, ":: error: link get is failing: %s\n",
119 			 rte_strerror(-link_get_err));
120 	if (link.link_status == RTE_ETH_LINK_DOWN)
121 		rte_exit(EXIT_FAILURE, ":: error: link is still down\n");
122 }
123 
124 static void
125 configure_port_template(uint16_t port_id)
126 {
127 	int ret;
128 	uint16_t std_queue;
129 	struct rte_flow_error error;
130 	struct rte_flow_queue_attr queue_attr[RTE_MAX_LCORE];
131 	const struct rte_flow_queue_attr *attr_list[RTE_MAX_LCORE];
132 	struct rte_flow_port_attr port_attr = { .nb_counters = 1 /* rules count */ };
133 
134 	for (std_queue = 0; std_queue < RTE_MAX_LCORE; std_queue++) {
135 		queue_attr[std_queue].size = MAX_QUEUE_SIZE;
136 		attr_list[std_queue] = &queue_attr[std_queue];
137 	}
138 
139 	ret = rte_flow_configure(port_id, &port_attr,
140 				 1, attr_list, &error);
141 	if (ret != 0)
142 		rte_exit(EXIT_FAILURE,
143 			 "rte_flow_configure:err=%d, port=%u\n",
144 			 ret, port_id);
145 	printf(":: Configuring template port [%d] Done ..\n", port_id);
146 }
147 
148 static void
149 init_port(void)
150 {
151 	int ret;
152 	uint16_t i;
153 	/* Ethernet port configured with default settings. */
154 	struct rte_eth_conf port_conf = {
155 		.txmode = {
156 			.offloads =
157 				RTE_ETH_TX_OFFLOAD_VLAN_INSERT |
158 				RTE_ETH_TX_OFFLOAD_IPV4_CKSUM  |
159 				RTE_ETH_TX_OFFLOAD_UDP_CKSUM   |
160 				RTE_ETH_TX_OFFLOAD_TCP_CKSUM   |
161 				RTE_ETH_TX_OFFLOAD_SCTP_CKSUM  |
162 				RTE_ETH_TX_OFFLOAD_TCP_TSO,
163 		},
164 	};
165 	struct rte_eth_txconf txq_conf;
166 	struct rte_eth_rxconf rxq_conf;
167 	struct rte_eth_dev_info dev_info;
168 
169 	ret = rte_eth_dev_info_get(port_id, &dev_info);
170 	if (ret != 0)
171 		rte_exit(EXIT_FAILURE,
172 			"Error during getting device (port %u) info: %s\n",
173 			port_id, strerror(-ret));
174 
175 	port_conf.txmode.offloads &= dev_info.tx_offload_capa;
176 	printf(":: initializing port: %d\n", port_id);
177 	ret = rte_eth_dev_configure(port_id,
178 				nr_queues, nr_queues, &port_conf);
179 	if (ret < 0) {
180 		rte_exit(EXIT_FAILURE,
181 			":: cannot configure device: err=%d, port=%u\n",
182 			ret, port_id);
183 	}
184 
185 	rxq_conf = dev_info.default_rxconf;
186 	rxq_conf.offloads = port_conf.rxmode.offloads;
187 
188 	/* Configuring number of RX and TX queues connected to single port. */
189 	for (i = 0; i < nr_queues; i++) {
190 		ret = rte_eth_rx_queue_setup(port_id, i, 512,
191 					 rte_eth_dev_socket_id(port_id),
192 					 &rxq_conf,
193 					 mbuf_pool);
194 		if (ret < 0) {
195 			rte_exit(EXIT_FAILURE,
196 				":: Rx queue setup failed: err=%d, port=%u\n",
197 				ret, port_id);
198 		}
199 	}
200 
201 	txq_conf = dev_info.default_txconf;
202 	txq_conf.offloads = port_conf.txmode.offloads;
203 
204 	for (i = 0; i < nr_queues; i++) {
205 		ret = rte_eth_tx_queue_setup(port_id, i, 512,
206 				rte_eth_dev_socket_id(port_id),
207 				&txq_conf);
208 		if (ret < 0) {
209 			rte_exit(EXIT_FAILURE,
210 				":: Tx queue setup failed: err=%d, port=%u\n",
211 				ret, port_id);
212 		}
213 	}
214 
215 	/* Setting the RX port to promiscuous mode. */
216 	ret = rte_eth_promiscuous_enable(port_id);
217 	if (ret != 0)
218 		rte_exit(EXIT_FAILURE,
219 			":: promiscuous mode enable failed: err=%s, port=%u\n",
220 			rte_strerror(-ret), port_id);
221 
222 	ret = rte_eth_dev_start(port_id);
223 	if (ret < 0) {
224 		rte_exit(EXIT_FAILURE,
225 			"rte_eth_dev_start:err=%d, port=%u\n",
226 			ret, port_id);
227 	}
228 
229 	assert_link_status();
230 
231 	printf(":: initializing port: %d done\n", port_id);
232 
233 	if (use_template_api == 0)
234 		return;
235 
236 	/* Adds rules engine configuration. 8< */
237 	ret = rte_eth_dev_stop(port_id);
238 	if (ret < 0)
239 		rte_exit(EXIT_FAILURE,
240 			"rte_eth_dev_stop:err=%d, port=%u\n",
241 			ret, port_id);
242 
243 	configure_port_template(port_id);
244 	ret = rte_eth_dev_start(port_id);
245 	if (ret < 0)
246 		rte_exit(EXIT_FAILURE,
247 			"rte_eth_dev_start:err=%d, port=%u\n",
248 			ret, port_id);
249 	/* >8 End of adding rules engine configuration. */
250 }
251 
252 static void
253 signal_handler(int signum)
254 {
255 	if (signum == SIGINT || signum == SIGTERM) {
256 		printf("\n\nSignal %d received, preparing to exit...\n",
257 				signum);
258 		force_quit = true;
259 	}
260 }
261 
262 /* Parse the argument given in the command line of the application */
263 static int
264 flow_filtering_parse_args(int argc, char **argv)
265 {
266 	static struct rte_argparse obj = {
267 		.prog_name = "flow_filtering",
268 		.usage = "[EAL options] -- [optional parameters]",
269 		.descriptor = NULL,
270 		.epilog = NULL,
271 		.exit_on_error = false,
272 		.callback = NULL,
273 		.opaque = NULL,
274 		.args = {
275 			{ "--template", NULL, "Enable template API flow",
276 			  &use_template_api, (void *)1,
277 			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
278 			},
279 			{ "--non-template", NULL, "Enable non template API flow",
280 			  &use_template_api, (void *)0,
281 			  RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT,
282 			},
283 			ARGPARSE_ARG_END(),
284 		},
285 	};
286 
287 	return rte_argparse_parse(&obj, argc, argv);
288 }
289 
290 int
291 main(int argc, char **argv)
292 {
293 	int ret;
294 	uint16_t nr_ports;
295 	struct rte_flow_error error;
296 
297 	/* Initialize EAL. 8< */
298 	ret = rte_eal_init(argc, argv);
299 	if (ret < 0)
300 		rte_exit(EXIT_FAILURE, ":: invalid EAL arguments\n");
301 	/* >8 End of Initialization of EAL. */
302 	argc -= ret;
303 	argv += ret;
304 
305 	force_quit = false;
306 	signal(SIGINT, signal_handler);
307 	signal(SIGTERM, signal_handler);
308 
309 	/* Parse application arguments (after the EAL ones) */
310 	ret = flow_filtering_parse_args(argc, argv);
311 	if (ret < 0)
312 		rte_exit(EXIT_FAILURE, "Invalid flow filtering arguments\n");
313 
314 	nr_ports = rte_eth_dev_count_avail();
315 	if (nr_ports == 0)
316 		rte_exit(EXIT_FAILURE, ":: no Ethernet ports found\n");
317 	port_id = 0;
318 	if (nr_ports != 1) {
319 		printf(":: warn: %d ports detected, but we use only one: port %u\n",
320 			nr_ports, port_id);
321 	}
322 
323 	/* Allocates a mempool to hold the mbufs. 8< */
324 	mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", 4096, 128, 0,
325 						RTE_MBUF_DEFAULT_BUF_SIZE,
326 						rte_socket_id());
327 	/* >8 End of allocating a mempool to hold the mbufs. */
328 	if (mbuf_pool == NULL)
329 		rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n");
330 
331 	/* Initializes all the ports using the user defined init_port(). 8< */
332 	init_port();
333 	/* >8 End of Initializing the ports using user defined init_port(). */
334 
335 	/* Function responsible for creating the flow rule. 8< */
336 	flow = generate_flow_skeleton(port_id, &error, use_template_api);
337 	/* >8 End of function responsible for creating the flow rule. */
338 
339 	if (!flow) {
340 		printf("Flow can't be created %d message: %s\n",
341 			error.type,
342 			error.message ? error.message : "(no stated reason)");
343 		rte_exit(EXIT_FAILURE, "error in creating flow");
344 	}
345 	printf("Flow created!!:\n");
346 
347 	/* Launching main_loop(). 8< */
348 	ret = main_loop();
349 	/* >8 End of launching main_loop(). */
350 
351 	/* clean up the EAL */
352 	rte_eal_cleanup();
353 
354 	return ret;
355 }
356