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