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