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