xref: /dpdk/examples/ipsec-secgw/flow.c (revision 8f1d23ece06adff5eae9f1b4365bdbbd3abee2b2)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (C) 2020 Marvell International Ltd.
3  */
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 
8 #include <rte_common.h>
9 #include <rte_flow.h>
10 #include <rte_ip.h>
11 
12 #include "flow.h"
13 #include "ipsec-secgw.h"
14 #include "parser.h"
15 
16 #define FLOW_RULES_MAX 128
17 
18 struct flow_rule_entry {
19 	uint8_t is_eth;
20 	uint8_t is_ipv4;
21 	uint8_t is_ipv6;
22 	RTE_STD_C11
23 	union {
24 		struct {
25 			struct rte_flow_item_ipv4 spec;
26 			struct rte_flow_item_ipv4 mask;
27 		} ipv4;
28 		struct {
29 			struct rte_flow_item_ipv6 spec;
30 			struct rte_flow_item_ipv6 mask;
31 		} ipv6;
32 	};
33 	struct rte_flow_item_mark mark_val;
34 	uint16_t port;
35 	uint16_t queue;
36 	bool is_queue_set;
37 	bool enable_count;
38 	bool enable_mark;
39 	bool set_security_action;
40 	bool set_mark_action;
41 	uint32_t mark_action_val;
42 	struct rte_flow *flow;
43 } flow_rule_tbl[FLOW_RULES_MAX];
44 
45 int nb_flow_rule;
46 
47 static void
48 ipv4_hdr_print(struct rte_ipv4_hdr *hdr)
49 {
50 	char a, b, c, d;
51 
52 	uint32_t_to_char(rte_bswap32(hdr->src_addr), &a, &b, &c, &d);
53 	printf("src: %3hhu.%3hhu.%3hhu.%3hhu \t", a, b, c, d);
54 
55 	uint32_t_to_char(rte_bswap32(hdr->dst_addr), &a, &b, &c, &d);
56 	printf("dst: %3hhu.%3hhu.%3hhu.%3hhu", a, b, c, d);
57 }
58 
59 static int
60 ipv4_addr_cpy(rte_be32_t *spec, rte_be32_t *mask, char *token,
61 	      struct parse_status *status)
62 {
63 	struct in_addr ip;
64 	uint32_t depth;
65 
66 	APP_CHECK(parse_ipv4_addr(token, &ip, &depth) == 0, status,
67 		 "unrecognized input \"%s\", expect valid ipv4 addr", token);
68 	if (status->status < 0)
69 		return -1;
70 
71 	if (depth > 32)
72 		return -1;
73 
74 	memcpy(mask, &rte_flow_item_ipv4_mask.hdr.src_addr, sizeof(ip));
75 
76 	*spec = ip.s_addr;
77 
78 	if (depth < 32)
79 		*mask = htonl(*mask << (32 - depth));
80 
81 	return 0;
82 }
83 
84 static void
85 ipv6_hdr_print(struct rte_ipv6_hdr *hdr)
86 {
87 	uint8_t *addr;
88 
89 	addr = hdr->src_addr;
90 	printf("src: %4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx \t",
91 	       (uint16_t)((addr[0] << 8) | addr[1]),
92 	       (uint16_t)((addr[2] << 8) | addr[3]),
93 	       (uint16_t)((addr[4] << 8) | addr[5]),
94 	       (uint16_t)((addr[6] << 8) | addr[7]),
95 	       (uint16_t)((addr[8] << 8) | addr[9]),
96 	       (uint16_t)((addr[10] << 8) | addr[11]),
97 	       (uint16_t)((addr[12] << 8) | addr[13]),
98 	       (uint16_t)((addr[14] << 8) | addr[15]));
99 
100 	addr = hdr->dst_addr;
101 	printf("dst: %4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx",
102 	       (uint16_t)((addr[0] << 8) | addr[1]),
103 	       (uint16_t)((addr[2] << 8) | addr[3]),
104 	       (uint16_t)((addr[4] << 8) | addr[5]),
105 	       (uint16_t)((addr[6] << 8) | addr[7]),
106 	       (uint16_t)((addr[8] << 8) | addr[9]),
107 	       (uint16_t)((addr[10] << 8) | addr[11]),
108 	       (uint16_t)((addr[12] << 8) | addr[13]),
109 	       (uint16_t)((addr[14] << 8) | addr[15]));
110 }
111 
112 static int
113 ipv6_addr_cpy(uint8_t *spec, uint8_t *mask, char *token,
114 	      struct parse_status *status)
115 {
116 	struct in6_addr ip;
117 	uint32_t depth, i;
118 
119 	APP_CHECK(parse_ipv6_addr(token, &ip, &depth) == 0, status,
120 		"unrecognized input \"%s\", expect valid ipv6 address", token);
121 	if (status->status < 0)
122 		return -1;
123 
124 	memcpy(mask, &rte_flow_item_ipv6_mask.hdr.src_addr, sizeof(ip));
125 	memcpy(spec, ip.s6_addr, sizeof(struct in6_addr));
126 
127 	for (i = 0; i < depth && (i%8 <= sizeof(struct in6_addr)); i++)
128 		mask[i/8] &= ~(1 << (7-i%8));
129 
130 	return 0;
131 }
132 
133 void
134 parse_flow_tokens(char **tokens, uint32_t n_tokens,
135 		  struct parse_status *status)
136 {
137 	struct flow_rule_entry *rule;
138 	uint32_t ti = 0;
139 
140 	if (nb_flow_rule >= FLOW_RULES_MAX) {
141 		printf("Too many flow rules\n");
142 		return;
143 	}
144 
145 	rule = &flow_rule_tbl[nb_flow_rule];
146 	memset(rule, 0, sizeof(*rule));
147 
148 	for (ti = 0; ti < n_tokens; ti++) {
149 		if (strcmp(tokens[ti], "mark") == 0) {
150 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
151 			if (status->status < 0)
152 				return;
153 			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
154 			if (status->status < 0)
155 				return;
156 
157 			rule->mark_val.id = atoi(tokens[ti]);
158 			rule->enable_mark = true;
159 			continue;
160 		}
161 		if (strcmp(tokens[ti], "eth") == 0) {
162 			rule->is_eth = true;
163 			continue;
164 		}
165 
166 		if (strcmp(tokens[ti], "ipv4") == 0) {
167 			rule->is_ipv4 = true;
168 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
169 			if (status->status < 0)
170 				return;
171 			if (strcmp(tokens[ti], "src") == 0) {
172 				INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
173 				if (status->status < 0)
174 					return;
175 				if (ipv4_addr_cpy(&rule->ipv4.spec.hdr.src_addr,
176 						  &rule->ipv4.mask.hdr.src_addr,
177 						  tokens[ti], status))
178 					return;
179 			}
180 			if (strcmp(tokens[ti], "dst") == 0) {
181 				INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
182 				if (status->status < 0)
183 					return;
184 				if (ipv4_addr_cpy(&rule->ipv4.spec.hdr.dst_addr,
185 						  &rule->ipv4.mask.hdr.dst_addr,
186 						  tokens[ti], status))
187 					return;
188 			}
189 			continue;
190 		}
191 		if (strcmp(tokens[ti], "ipv6") == 0) {
192 			rule->is_ipv6 = true;
193 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
194 			if (status->status < 0)
195 				return;
196 			if (strcmp(tokens[ti], "src") == 0) {
197 				INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
198 				if (status->status < 0)
199 					return;
200 				if (ipv6_addr_cpy(rule->ipv6.spec.hdr.src_addr,
201 						  rule->ipv6.mask.hdr.src_addr,
202 						  tokens[ti], status))
203 					return;
204 			}
205 			if (strcmp(tokens[ti], "dst") == 0) {
206 				INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
207 				if (status->status < 0)
208 					return;
209 				if (ipv6_addr_cpy(rule->ipv6.spec.hdr.dst_addr,
210 						  rule->ipv6.mask.hdr.dst_addr,
211 						  tokens[ti], status))
212 					return;
213 			}
214 			continue;
215 		}
216 
217 		if (strcmp(tokens[ti], "port") == 0) {
218 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
219 			if (status->status < 0)
220 				return;
221 			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
222 			if (status->status < 0)
223 				return;
224 
225 			rule->port = atoi(tokens[ti]);
226 			continue;
227 		}
228 
229 		if (strcmp(tokens[ti], "queue") == 0) {
230 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
231 			if (status->status < 0)
232 				return;
233 			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
234 			if (status->status < 0)
235 				return;
236 
237 			rule->queue = atoi(tokens[ti]);
238 			rule->is_queue_set = true;
239 			continue;
240 		}
241 
242 		if (strcmp(tokens[ti], "count") == 0) {
243 			rule->enable_count = true;
244 			continue;
245 		}
246 
247 		if (strcmp(tokens[ti], "security") == 0) {
248 			rule->set_security_action = true;
249 			continue;
250 		}
251 		if (strcmp(tokens[ti], "set_mark") == 0) {
252 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
253 			if (status->status < 0)
254 				return;
255 			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
256 			if (status->status < 0)
257 				return;
258 
259 			rule->set_mark_action = true;
260 			rule->mark_action_val = atoi(tokens[ti]);
261 			continue;
262 		}
263 
264 		sprintf(status->parse_msg, "Unrecognized input:%s\n",
265 			tokens[ti]);
266 		status->status = -1;
267 		return;
268 	}
269 	printf("\n");
270 
271 	nb_flow_rule++;
272 }
273 
274 #define MAX_RTE_FLOW_PATTERN (4)
275 #define MAX_RTE_FLOW_ACTIONS (5)
276 
277 static void
278 flow_init_single(struct flow_rule_entry *rule)
279 {
280 	struct rte_flow_action action[MAX_RTE_FLOW_ACTIONS] = {};
281 	struct rte_flow_item pattern[MAX_RTE_FLOW_PATTERN] = {};
282 	struct rte_flow_action_queue queue_action;
283 	struct rte_flow_action_mark mark_action;
284 	int ret, pattern_idx = 0, act_idx = 0;
285 	struct rte_flow_item_mark mark_mask;
286 	struct rte_flow_attr attr = {};
287 	struct rte_flow_error err = {};
288 
289 	attr.egress = 0;
290 	attr.ingress = 1;
291 
292 	if (rule->is_queue_set) {
293 		queue_action.index = rule->queue;
294 		action[act_idx].type = RTE_FLOW_ACTION_TYPE_QUEUE;
295 		action[act_idx].conf = &queue_action;
296 		act_idx++;
297 	}
298 
299 	if (rule->enable_count) {
300 		action[act_idx].type = RTE_FLOW_ACTION_TYPE_COUNT;
301 		act_idx++;
302 	}
303 
304 	if (rule->set_security_action) {
305 		action[act_idx].type = RTE_FLOW_ACTION_TYPE_SECURITY;
306 		action[act_idx].conf = NULL;
307 		act_idx++;
308 	}
309 
310 	if (rule->set_mark_action) {
311 		mark_action.id = rule->mark_action_val;
312 		action[act_idx].type = RTE_FLOW_ACTION_TYPE_MARK;
313 		action[act_idx].conf = &mark_action;
314 		act_idx++;
315 	}
316 
317 	action[act_idx].type = RTE_FLOW_ACTION_TYPE_END;
318 	action[act_idx].conf = NULL;
319 
320 	if (rule->enable_mark) {
321 		mark_mask.id = UINT32_MAX;
322 		pattern[pattern_idx].type = RTE_FLOW_ITEM_TYPE_MARK;
323 		pattern[pattern_idx].spec = &rule->mark_val;
324 		pattern[pattern_idx].mask = &mark_mask;
325 		pattern_idx++;
326 	}
327 
328 	if (rule->is_eth) {
329 		pattern[pattern_idx].type = RTE_FLOW_ITEM_TYPE_ETH;
330 		pattern_idx++;
331 	}
332 
333 	if (rule->is_ipv4) {
334 		pattern[pattern_idx].type = RTE_FLOW_ITEM_TYPE_IPV4;
335 		pattern[pattern_idx].spec = &rule->ipv4.spec;
336 		pattern[pattern_idx].mask = &rule->ipv4.mask;
337 		pattern_idx++;
338 	}
339 
340 	if (rule->is_ipv6) {
341 		pattern[pattern_idx].type = RTE_FLOW_ITEM_TYPE_IPV6;
342 		pattern[pattern_idx].spec = &rule->ipv6.spec;
343 		pattern[pattern_idx].mask = &rule->ipv6.mask;
344 		pattern_idx++;
345 	}
346 
347 	if (rule->set_security_action) {
348 		pattern[pattern_idx].type = RTE_FLOW_ITEM_TYPE_ESP;
349 		pattern[pattern_idx].spec = NULL;
350 		pattern[pattern_idx].mask = NULL;
351 		pattern[pattern_idx].last = NULL;
352 		pattern_idx++;
353 	}
354 
355 	pattern[pattern_idx].type = RTE_FLOW_ITEM_TYPE_END;
356 
357 	ret = rte_flow_validate(rule->port, &attr, pattern, action, &err);
358 	if (ret < 0) {
359 		RTE_LOG(ERR, IPSEC, "Flow validation failed %s\n", err.message);
360 		rule->flow = 0;
361 		return;
362 	}
363 
364 	rule->flow = rte_flow_create(rule->port, &attr, pattern, action, &err);
365 	if (rule->flow == NULL)
366 		RTE_LOG(ERR, IPSEC, "Flow creation return %s\n", err.message);
367 }
368 
369 void
370 flow_print_counters(void)
371 {
372 	struct rte_flow_query_count count_query;
373 	struct rte_flow_action action;
374 	struct flow_rule_entry *rule;
375 	struct rte_flow_error error;
376 	int i = 0, ret = 0;
377 
378 	action.type = RTE_FLOW_ACTION_TYPE_COUNT;
379 
380 	for (i = 0; i < nb_flow_rule; i++) {
381 		rule = &flow_rule_tbl[i];
382 		if (!rule->flow || !rule->enable_count)
383 			continue;
384 
385 		/* Poisoning to make sure PMDs update it in case of error. */
386 		memset(&error, 0x55, sizeof(error));
387 		memset(&count_query, 0, sizeof(count_query));
388 		ret = rte_flow_query(rule->port, rule->flow, &action,
389 				     &count_query, &error);
390 		if (ret)
391 			RTE_LOG(ERR, IPSEC,
392 				"Failed to get flow counter "
393 				" for port %u, err msg: %s\n",
394 				rule->port, error.message);
395 
396 		printf("Flow #%3d:", i);
397 		if (rule->is_ipv4) {
398 			printf(" spec ipv4 ");
399 			ipv4_hdr_print(&rule->ipv4.spec.hdr);
400 		}
401 		if (rule->is_ipv6) {
402 			printf(" spec ipv6 ");
403 			ipv6_hdr_print(&rule->ipv6.spec.hdr);
404 		}
405 
406 		if (rule->set_security_action)
407 			printf(" Security action set,");
408 
409 		if (rule->enable_mark)
410 			printf(" Mark Enabled");
411 
412 		printf(" Port: %d,", rule->port);
413 		if (rule->is_queue_set)
414 			printf(" Queue: %d", rule->queue);
415 		printf(" Hits: %"PRIu64"\n", count_query.hits);
416 	}
417 }
418 
419 void
420 flow_init(void)
421 {
422 	struct flow_rule_entry *rule;
423 	int i;
424 
425 	for (i = 0; i < nb_flow_rule; i++) {
426 		rule = &flow_rule_tbl[i];
427 		flow_init_single(rule);
428 	}
429 
430 	for (i = 0; i < nb_flow_rule; i++) {
431 		rule = &flow_rule_tbl[i];
432 		printf("Flow #%3d: ", i);
433 		if (rule->is_ipv4) {
434 			printf("spec ipv4 ");
435 			ipv4_hdr_print(&rule->ipv4.spec.hdr);
436 			printf("\n");
437 			printf(" mask ipv4 ");
438 			ipv4_hdr_print(&rule->ipv4.mask.hdr);
439 		}
440 		if (rule->is_ipv6) {
441 			printf("spec ipv6 ");
442 			ipv6_hdr_print(&rule->ipv6.spec.hdr);
443 			printf("\n");
444 			printf(" mask ipv6 ");
445 			ipv6_hdr_print(&rule->ipv6.mask.hdr);
446 		}
447 
448 		if (rule->enable_mark)
449 			printf(", Mark enabled");
450 
451 		printf("\tPort: %d,", rule->port);
452 		if (rule->is_queue_set)
453 			printf(" Queue: %d,", rule->queue);
454 
455 		if (rule->set_security_action)
456 			printf(" Security action set,");
457 
458 		if (rule->set_mark_action)
459 			printf(" Mark: %d,", rule->mark_action_val);
460 
461 		if (rule->enable_count)
462 			printf(" Counter enabled,");
463 
464 		if (rule->flow == NULL)
465 			printf(" [UNSUPPORTED]");
466 		printf("\n");
467 	}
468 }
469