xref: /dpdk/app/test-pmd/cmd_flex_item.c (revision 71213a8b)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2021 NVIDIA Corporation & Affiliates
3  */
4 
5 #include <stddef.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <errno.h>
9 #include <string.h>
10 
11 #include <rte_common.h>
12 #include <rte_ethdev.h>
13 #include <cmdline_parse.h>
14 #include <cmdline_parse_string.h>
15 #include <cmdline_parse_num.h>
16 #include <rte_flow.h>
17 
18 #include "testpmd.h"
19 
20 struct flex_item *flex_items[RTE_MAX_ETHPORTS][FLEX_MAX_PARSERS_NUM];
21 struct flex_pattern flex_patterns[FLEX_MAX_PATTERNS_NUM];
22 
23 #ifdef RTE_HAS_JANSSON
24 static __rte_always_inline bool
25 match_strkey(const char *key, const char *pattern)
26 {
27 	return strncmp(key, pattern, strlen(key)) == 0;
28 }
29 
30 static struct flex_item *
31 flex_parser_fetch(uint16_t port_id, uint16_t flex_id)
32 {
33 	if (port_id >= RTE_MAX_ETHPORTS) {
34 		printf("Invalid port_id: %u\n", port_id);
35 		return FLEX_PARSER_ERR;
36 	}
37 	if (flex_id >= FLEX_MAX_PARSERS_NUM) {
38 		printf("Invalid flex item flex_id: %u\n", flex_id);
39 		return FLEX_PARSER_ERR;
40 	}
41 	return flex_items[port_id][flex_id];
42 }
43 
44 void
45 flex_item_destroy(portid_t port_id, uint16_t flex_id)
46 {
47 	int ret;
48 	struct rte_flow_error error;
49 	struct flex_item *fp = flex_parser_fetch(port_id, flex_id);
50 	if (fp == FLEX_PARSER_ERR) {
51 		printf("Bad parameters: port_id=%u flex_id=%u\n",
52 		       port_id, flex_id);
53 		return;
54 	}
55 	if (!fp)
56 		return;
57 	ret = rte_flow_flex_item_release(port_id, fp->flex_handle, &error);
58 	if (!ret) {
59 		free(fp);
60 		flex_items[port_id][flex_id] = NULL;
61 		printf("port-%u: released flex item #%u\n",
62 		       port_id, flex_id);
63 
64 	} else {
65 		printf("port-%u: cannot release flex item #%u: %s\n",
66 		       port_id, flex_id, error.message);
67 	}
68 }
69 
70 static int
71 flex_tunnel_parse(json_t *jtun, enum rte_flow_item_flex_tunnel_mode *tunnel)
72 {
73 	int tun = -1;
74 
75 	if (json_is_integer(jtun))
76 		tun = (int)json_integer_value(jtun);
77 	else if (json_is_real(jtun))
78 		tun = (int)json_real_value(jtun);
79 	else if (json_is_string(jtun)) {
80 		const char *mode = json_string_value(jtun);
81 
82 		if (match_strkey(mode, "FLEX_TUNNEL_MODE_SINGLE"))
83 			tun = FLEX_TUNNEL_MODE_SINGLE;
84 		else if (match_strkey(mode, "FLEX_TUNNEL_MODE_OUTER"))
85 			tun = FLEX_TUNNEL_MODE_OUTER;
86 		else if (match_strkey(mode, "FLEX_TUNNEL_MODE_INNER"))
87 			tun = FLEX_TUNNEL_MODE_INNER;
88 		else if (match_strkey(mode, "FLEX_TUNNEL_MODE_MULTI"))
89 			tun = FLEX_TUNNEL_MODE_MULTI;
90 		else if (match_strkey(mode, "FLEX_TUNNEL_MODE_TUNNEL"))
91 			tun = FLEX_TUNNEL_MODE_TUNNEL;
92 		else
93 			return -EINVAL;
94 	} else
95 		return -EINVAL;
96 	*tunnel = (enum rte_flow_item_flex_tunnel_mode)tun;
97 	return 0;
98 }
99 
100 static int
101 flex_field_parse(json_t *jfld, struct rte_flow_item_flex_field *fld)
102 {
103 	const char *key;
104 	json_t *je;
105 
106 #define FLEX_FIELD_GET(fm, t) \
107 do {                  \
108 	if (!strncmp(key, # fm, strlen(# fm))) { \
109 		if (json_is_real(je))   \
110 			fld->fm = (t) json_real_value(je); \
111 		else if (json_is_integer(je))   \
112 			fld->fm = (t) json_integer_value(je); \
113 		else   \
114 			return -EINVAL; \
115 	}         \
116 } while (0)
117 
118 	json_object_foreach(jfld, key, je) {
119 		FLEX_FIELD_GET(field_size, uint32_t);
120 		FLEX_FIELD_GET(field_base, int32_t);
121 		FLEX_FIELD_GET(offset_base, uint32_t);
122 		FLEX_FIELD_GET(offset_mask, uint32_t);
123 		FLEX_FIELD_GET(offset_shift, int32_t);
124 		FLEX_FIELD_GET(field_id, uint16_t);
125 		if (match_strkey(key, "field_mode")) {
126 			const char *mode;
127 			if (!json_is_string(je))
128 				return -EINVAL;
129 			mode = json_string_value(je);
130 			if (match_strkey(mode, "FIELD_MODE_DUMMY"))
131 				fld->field_mode = FIELD_MODE_DUMMY;
132 			else if (match_strkey(mode, "FIELD_MODE_FIXED"))
133 				fld->field_mode = FIELD_MODE_FIXED;
134 			else if (match_strkey(mode, "FIELD_MODE_OFFSET"))
135 				fld->field_mode = FIELD_MODE_OFFSET;
136 			else if (match_strkey(mode, "FIELD_MODE_BITMASK"))
137 				fld->field_mode = FIELD_MODE_BITMASK;
138 			else
139 				return -EINVAL;
140 		}
141 	}
142 	return 0;
143 }
144 
145 enum flex_link_type {
146 	FLEX_LINK_IN = 0,
147 	FLEX_LINK_OUT = 1
148 };
149 
150 static int
151 flex_link_item_parse(const char *src, struct rte_flow_item *item)
152 {
153 #define  FLEX_PARSE_DATA_SIZE 1024
154 
155 	int ret;
156 	uint8_t *ptr, data[FLEX_PARSE_DATA_SIZE] = {0,};
157 	char flow_rule[256];
158 	struct rte_flow_attr *attr;
159 	struct rte_flow_item *pattern;
160 	struct rte_flow_action *actions;
161 
162 	sprintf(flow_rule, "flow create 0 pattern %s / end", src);
163 	src = flow_rule;
164 	ret = flow_parse(src, (void *)data, sizeof(data),
165 			 &attr, &pattern, &actions);
166 	if (ret)
167 		return ret;
168 	item->type = pattern->type;
169 	if (pattern->spec) {
170 		ptr = (void *)(uintptr_t)item->spec;
171 		memcpy(ptr, pattern->spec, FLEX_MAX_FLOW_PATTERN_LENGTH);
172 	} else {
173 		item->spec = NULL;
174 	}
175 	if (pattern->mask) {
176 		ptr = (void *)(uintptr_t)item->mask;
177 		memcpy(ptr, pattern->mask, FLEX_MAX_FLOW_PATTERN_LENGTH);
178 	} else {
179 		item->mask = NULL;
180 	}
181 	if (pattern->last) {
182 		ptr = (void *)(uintptr_t)item->last;
183 		memcpy(ptr, pattern->last, FLEX_MAX_FLOW_PATTERN_LENGTH);
184 	} else {
185 		item->last = NULL;
186 	}
187 	return 0;
188 }
189 
190 static int
191 flex_link_parse(json_t *jobj, struct rte_flow_item_flex_link *link,
192 		enum flex_link_type link_type)
193 {
194 	const char *key;
195 	json_t *je;
196 	int ret;
197 	json_object_foreach(jobj, key, je) {
198 		if (match_strkey(key, "item")) {
199 			if (!json_is_string(je))
200 				return -EINVAL;
201 			ret = flex_link_item_parse(json_string_value(je),
202 						   &link->item);
203 			if (ret)
204 				return -EINVAL;
205 			if (link_type == FLEX_LINK_IN) {
206 				if (!link->item.spec || !link->item.mask)
207 					return -EINVAL;
208 				if (link->item.last)
209 					return -EINVAL;
210 			}
211 		}
212 		if (match_strkey(key, "next")) {
213 			if (json_is_integer(je))
214 				link->next = (typeof(link->next))
215 					     json_integer_value(je);
216 			else if (json_is_real(je))
217 				link->next = (typeof(link->next))
218 					     json_real_value(je);
219 			else
220 				return -EINVAL;
221 		}
222 	}
223 	return 0;
224 }
225 
226 static int flex_item_config(json_t *jroot,
227 			    struct rte_flow_item_flex_conf *flex_conf)
228 {
229 	const char *key;
230 	json_t *jobj = NULL;
231 	int ret = 0;
232 
233 	json_object_foreach(jroot, key, jobj) {
234 		if (match_strkey(key, "tunnel")) {
235 			ret = flex_tunnel_parse(jobj, &flex_conf->tunnel);
236 			if (ret) {
237 				printf("Can't parse tunnel value\n");
238 				goto out;
239 			}
240 		} else if (match_strkey(key, "next_header")) {
241 			ret = flex_field_parse(jobj, &flex_conf->next_header);
242 			if (ret) {
243 				printf("Can't parse next_header field\n");
244 				goto out;
245 			}
246 		} else if (match_strkey(key, "next_protocol")) {
247 			ret = flex_field_parse(jobj,
248 					       &flex_conf->next_protocol);
249 			if (ret) {
250 				printf("Can't parse next_protocol field\n");
251 				goto out;
252 			}
253 		} else if (match_strkey(key, "sample_data")) {
254 			json_t *ji;
255 			uint32_t i, size = json_array_size(jobj);
256 			for (i = 0; i < size; i++) {
257 				ji = json_array_get(jobj, i);
258 				ret = flex_field_parse
259 					(ji, flex_conf->sample_data + i);
260 				if (ret) {
261 					printf("Can't parse sample_data field(s)\n");
262 					goto out;
263 				}
264 			}
265 			flex_conf->nb_samples = size;
266 		} else if (match_strkey(key, "input_link")) {
267 			json_t *ji;
268 			uint32_t i, size = json_array_size(jobj);
269 			for (i = 0; i < size; i++) {
270 				ji = json_array_get(jobj, i);
271 				ret = flex_link_parse(ji,
272 						      flex_conf->input_link + i,
273 						      FLEX_LINK_IN);
274 				if (ret) {
275 					printf("Can't parse input_link(s)\n");
276 					goto out;
277 				}
278 			}
279 			flex_conf->nb_inputs = size;
280 		} else if (match_strkey(key, "output_link")) {
281 			json_t *ji;
282 			uint32_t i, size = json_array_size(jobj);
283 			for (i = 0; i < size; i++) {
284 				ji = json_array_get(jobj, i);
285 				ret = flex_link_parse
286 					(ji, flex_conf->output_link + i,
287 					 FLEX_LINK_OUT);
288 				if (ret) {
289 					printf("Can't parse output_link(s)\n");
290 					goto out;
291 				}
292 			}
293 			flex_conf->nb_outputs = size;
294 		}
295 	}
296 out:
297 	return ret;
298 }
299 
300 static struct flex_item *
301 flex_item_init(void)
302 {
303 	size_t base_size, samples_size, links_size, spec_size;
304 	struct rte_flow_item_flex_conf *conf;
305 	struct flex_item *fp;
306 	uint8_t (*pattern)[FLEX_MAX_FLOW_PATTERN_LENGTH];
307 	int i;
308 
309 	base_size = RTE_ALIGN(sizeof(*conf), sizeof(uintptr_t));
310 	samples_size = RTE_ALIGN(FLEX_ITEM_MAX_SAMPLES_NUM *
311 				 sizeof(conf->sample_data[0]),
312 				 sizeof(uintptr_t));
313 	links_size = RTE_ALIGN(FLEX_ITEM_MAX_LINKS_NUM *
314 			       sizeof(conf->input_link[0]),
315 			       sizeof(uintptr_t));
316 	/* spec & mask for all input links */
317 	spec_size = 2 * FLEX_MAX_FLOW_PATTERN_LENGTH * FLEX_ITEM_MAX_LINKS_NUM;
318 	fp = calloc(1, base_size + samples_size + 2 * links_size + spec_size);
319 	if (fp == NULL) {
320 		printf("Can't allocate memory for flex item\n");
321 		return NULL;
322 	}
323 	conf = &fp->flex_conf;
324 	conf->sample_data = (typeof(conf->sample_data))
325 			    ((uint8_t *)fp + base_size);
326 	conf->input_link = (typeof(conf->input_link))
327 			   ((uint8_t *)conf->sample_data + samples_size);
328 	conf->output_link = (typeof(conf->output_link))
329 			    ((uint8_t *)conf->input_link + links_size);
330 	pattern = (typeof(pattern))((uint8_t *)conf->output_link + links_size);
331 	for (i = 0; i < FLEX_ITEM_MAX_LINKS_NUM; i++) {
332 		struct rte_flow_item_flex_link *in = conf->input_link + i;
333 		in->item.spec = pattern++;
334 		in->item.mask = pattern++;
335 	}
336 	return fp;
337 }
338 
339 static int
340 flex_item_build_config(struct flex_item *fp, const char *filename)
341 {
342 	int ret;
343 	json_error_t json_error;
344 	json_t *jroot = json_load_file(filename, 0, &json_error);
345 
346 	if (!jroot) {
347 		printf("Bad JSON file \"%s\": %s\n", filename, json_error.text);
348 		return -1;
349 	}
350 	ret = flex_item_config(jroot, &fp->flex_conf);
351 	json_decref(jroot);
352 	return ret;
353 }
354 
355 void
356 flex_item_create(portid_t port_id, uint16_t flex_id, const char *filename)
357 {
358 	struct rte_flow_error flow_error;
359 	struct flex_item *fp = flex_parser_fetch(port_id, flex_id);
360 	int ret;
361 
362 	if (fp == FLEX_PARSER_ERR) {
363 		printf("Bad parameters: port_id=%u flex_id=%u\n",
364 		       port_id, flex_id);
365 		return;
366 	}
367 	if (fp) {
368 		printf("port-%u: flex item #%u is already in use\n",
369 		       port_id, flex_id);
370 		return;
371 	}
372 	fp = flex_item_init();
373 	if (!fp) {
374 		printf("Could not allocate flex item\n");
375 		goto out;
376 	}
377 	ret = flex_item_build_config(fp, filename);
378 	if (ret)
379 		goto out;
380 	fp->flex_handle = rte_flow_flex_item_create(port_id,
381 						    &fp->flex_conf,
382 						    &flow_error);
383 	if (fp->flex_handle) {
384 		flex_items[port_id][flex_id] = fp;
385 		printf("port-%u: created flex item #%u\n", port_id, flex_id);
386 		fp = NULL;
387 	} else {
388 		printf("port-%u: flex item #%u creation failed: %s\n",
389 		       port_id, flex_id,
390 		       flow_error.message ? flow_error.message : "");
391 	}
392 out:
393 	if (fp)
394 		free(fp);
395 }
396 
397 #else /* RTE_HAS_JANSSON */
398 void flex_item_create(__rte_unused portid_t port_id,
399 		      __rte_unused uint16_t flex_id,
400 		      __rte_unused const char *filename)
401 {
402 	printf("no JSON library\n");
403 }
404 
405 void flex_item_destroy(__rte_unused portid_t port_id,
406 		       __rte_unused uint16_t flex_id)
407 {
408 	printf("no JSON library\n");
409 }
410 #endif /* RTE_HAS_JANSSON */
411 
412 void
413 port_flex_item_flush(portid_t port_id)
414 {
415 	uint16_t i;
416 
417 	for (i = 0; i < FLEX_MAX_PARSERS_NUM; i++) {
418 		flex_item_destroy(port_id, i);
419 		flex_items[port_id][i] = NULL;
420 	}
421 }
422 
423 struct flex_pattern_set {
424 	cmdline_fixed_string_t set, flex_pattern;
425 	cmdline_fixed_string_t is_spec, mask;
426 	cmdline_fixed_string_t spec_data, mask_data;
427 	uint16_t id;
428 };
429 
430 static cmdline_parse_token_string_t flex_pattern_set_token =
431 	TOKEN_STRING_INITIALIZER(struct flex_pattern_set, set, "set");
432 static cmdline_parse_token_string_t flex_pattern_token =
433 	TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
434 flex_pattern, "flex_pattern");
435 static cmdline_parse_token_string_t flex_pattern_is_token =
436 	TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
437 is_spec, "is");
438 static cmdline_parse_token_string_t flex_pattern_spec_token =
439 	TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
440 is_spec, "spec");
441 static cmdline_parse_token_string_t flex_pattern_mask_token =
442 	TOKEN_STRING_INITIALIZER(struct flex_pattern_set, mask, "mask");
443 static cmdline_parse_token_string_t flex_pattern_spec_data_token =
444 	TOKEN_STRING_INITIALIZER(struct flex_pattern_set, spec_data, NULL);
445 static cmdline_parse_token_string_t flex_pattern_mask_data_token =
446 	TOKEN_STRING_INITIALIZER(struct flex_pattern_set, mask_data, NULL);
447 static cmdline_parse_token_num_t flex_pattern_id_token =
448 	TOKEN_NUM_INITIALIZER(struct flex_pattern_set, id, RTE_UINT16);
449 
450 /*
451  * flex pattern data - spec or mask is a string representation of byte array
452  * in hexadecimal format. Each byte in data string must have 2 characters:
453  * 0x15 - "15"
454  * 0x1  - "01"
455  * Bytes in data array are in network order.
456  */
457 static uint32_t
458 flex_pattern_data(const char *str, uint8_t *data)
459 {
460 	uint32_t i, len = strlen(str);
461 	char b[3], *endptr;
462 
463 	if (len & 01)
464 		return 0;
465 	len /= 2;
466 	if (len >= FLEX_MAX_FLOW_PATTERN_LENGTH)
467 		return 0;
468 	for (i = 0, b[2] = '\0'; i < len; i++) {
469 		b[0] = str[2 * i];
470 		b[1] = str[2 * i + 1];
471 		data[i] = strtoul(b, &endptr, 16);
472 		if (endptr != &b[2])
473 			return 0;
474 	}
475 	return len;
476 }
477 
478 static void
479 flex_pattern_parsed_fn(void *parsed_result,
480 		       __rte_unused struct cmdline *cl,
481 		       __rte_unused void *data)
482 {
483 	struct flex_pattern_set *res = parsed_result;
484 	struct flex_pattern *fp;
485 	bool full_spec;
486 
487 	if (res->id >= FLEX_MAX_PATTERNS_NUM) {
488 		printf("Bad flex pattern id\n");
489 		return;
490 	}
491 	fp = flex_patterns + res->id;
492 	memset(fp->spec_pattern, 0, sizeof(fp->spec_pattern));
493 	memset(fp->mask_pattern, 0, sizeof(fp->mask_pattern));
494 	fp->spec.length = flex_pattern_data(res->spec_data, fp->spec_pattern);
495 	if (!fp->spec.length) {
496 		printf("Bad flex pattern spec\n");
497 		return;
498 	}
499 	full_spec = strncmp(res->is_spec, "spec", strlen("spec")) == 0;
500 	if (full_spec) {
501 		fp->mask.length = flex_pattern_data(res->mask_data,
502 						    fp->mask_pattern);
503 		if (!fp->mask.length) {
504 			printf("Bad flex pattern mask\n");
505 			return;
506 		}
507 	} else {
508 		memset(fp->mask_pattern, 0xFF, fp->spec.length);
509 		fp->mask.length = fp->spec.length;
510 	}
511 	if (fp->mask.length != fp->spec.length) {
512 		printf("Spec length do not match mask length\n");
513 		return;
514 	}
515 	fp->spec.pattern = fp->spec_pattern;
516 	fp->mask.pattern = fp->mask_pattern;
517 	printf("created pattern #%u\n", res->id);
518 }
519 
520 cmdline_parse_inst_t cmd_set_flex_is_pattern = {
521 	.f = flex_pattern_parsed_fn,
522 	.data = NULL,
523 	.help_str = "set flex_pattern <id> is <spec_data>",
524 	.tokens = {
525 		(void *)&flex_pattern_set_token,
526 		(void *)&flex_pattern_token,
527 		(void *)&flex_pattern_id_token,
528 		(void *)&flex_pattern_is_token,
529 		(void *)&flex_pattern_spec_data_token,
530 		NULL,
531 	}
532 };
533 
534 cmdline_parse_inst_t cmd_set_flex_spec_pattern = {
535 	.f = flex_pattern_parsed_fn,
536 	.data = NULL,
537 	.help_str = "set flex_pattern <id> spec <spec_data> mask <mask_data>",
538 	.tokens = {
539 		(void *)&flex_pattern_set_token,
540 		(void *)&flex_pattern_token,
541 		(void *)&flex_pattern_id_token,
542 		(void *)&flex_pattern_spec_token,
543 		(void *)&flex_pattern_spec_data_token,
544 		(void *)&flex_pattern_mask_token,
545 		(void *)&flex_pattern_mask_data_token,
546 		NULL,
547 	}
548 };
549