/
phprus
/
github_dpdk
Обзор
Документация
Войти
/
phprus
/
github_dpdk
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
drivers/net/intel/cpfl/cpfl_flow_parser.c
2 170 строк
54 KB
Bruce Richardson
net/intel: fix use of non-recommended string functions
26 июн 2026, 11:10
26 июн 2026, 11:10
11b3f7b
Код
Авторство
О чём код?
/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2023 Intel Corporation */ #include <arpa/inet.h> #include "cpfl_flow_parser.h" static enum rte_flow_item_type cpfl_get_item_type_by_str(const char *type) { if (strcmp(type, "eth") == 0) return RTE_FLOW_ITEM_TYPE_ETH; else if (strcmp(type, "ipv4") == 0) return RTE_FLOW_ITEM_TYPE_IPV4; else if (strcmp(type, "tcp") == 0) return RTE_FLOW_ITEM_TYPE_TCP; else if (strcmp(type, "udp") == 0) return RTE_FLOW_ITEM_TYPE_UDP; else if (strcmp(type, "vxlan") == 0) return RTE_FLOW_ITEM_TYPE_VXLAN; else if (strcmp(type, "icmp") == 0) return RTE_FLOW_ITEM_TYPE_ICMP; else if (strcmp(type, "vlan") == 0) return RTE_FLOW_ITEM_TYPE_VLAN; PMD_DRV_LOG(ERR, "Not support this type: %s.", type); return RTE_FLOW_ITEM_TYPE_VOID; } static enum rte_flow_action_type cpfl_get_action_type_by_str(const char *type) { if (strcmp(type, "vxlan_encap") == 0) return RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP; else if (strcmp(type, "vxlan_decap") == 0) return RTE_FLOW_ACTION_TYPE_VXLAN_DECAP; else if (strcmp(type, "prog") == 0) return RTE_FLOW_ACTION_TYPE_PROG; PMD_DRV_LOG(ERR, "Not support this type: %s.", type); return RTE_FLOW_ACTION_TYPE_VOID; } static const char * cpfl_json_t_to_string(json_t *object, const char *name) { json_t *subobject; if (!object) { PMD_DRV_LOG(ERR, "object doesn't exist."); return NULL; } subobject = json_object_get(object, name); if (!subobject) { PMD_DRV_LOG(ERR, "%s doesn't exist.", name); return NULL; } return json_string_value(subobject); } static int cpfl_json_t_to_int(json_t *object, const char *name, int *value) { json_t *subobject; if (!object) { PMD_DRV_LOG(ERR, "object doesn't exist."); return -EINVAL; } subobject = json_object_get(object, name); if (!subobject) { PMD_DRV_LOG(ERR, "%s doesn't exist.", name); return -EINVAL; } if (!json_is_integer(subobject)) { PMD_DRV_LOG(ERR, "%s is not an integer.", name); return -EINVAL; } *value = (int)json_integer_value(subobject); return 0; } static int cpfl_json_t_to_uint16(json_t *object, const char *name, uint16_t *value) { json_t *subobject; if (!object) { PMD_DRV_LOG(ERR, "object doesn't exist."); return -EINVAL; } subobject = json_object_get(object, name); if (!subobject) { PMD_DRV_LOG(ERR, "%s doesn't exist.", name); return -EINVAL; } if (!json_is_integer(subobject)) { PMD_DRV_LOG(ERR, "%s is not an integer.", name); return -EINVAL; } *value = (uint16_t)json_integer_value(subobject); return 0; } static int cpfl_json_t_to_uint32(json_t *object, const char *name, uint32_t *value) { json_t *subobject; if (!object) { PMD_DRV_LOG(ERR, "object doesn't exist."); return -EINVAL; } subobject = json_object_get(object, name); if (!subobject) { PMD_DRV_LOG(ERR, "%s doesn't exist.", name); return -EINVAL; } if (!json_is_integer(subobject)) { PMD_DRV_LOG(ERR, "%s is not an integer.", name); return -EINVAL; } *value = (uint32_t)json_integer_value(subobject); return 0; } static int cpfl_flow_js_pattern_key_attr(json_t *ob_pr_key_attrs, struct cpfl_flow_js_pr *js_pr) { int i, len; struct cpfl_flow_js_pr_key_attr *attr; len = json_array_size(ob_pr_key_attrs); js_pr->key.attributes = rte_malloc(NULL, sizeof(struct cpfl_flow_js_pr_key_attr), 0); if (!js_pr->key.attributes) { PMD_DRV_LOG(ERR, "Failed to alloc memory."); return -ENOMEM; } js_pr->key.attr_size = len; attr = js_pr->key.attributes; for (i = 0; i < len; i++) { json_t *object; const char *name; uint16_t value = 0; int ret; object = json_array_get(ob_pr_key_attrs, i); name = cpfl_json_t_to_string(object, "Name"); if (!name) { PMD_DRV_LOG(ERR, "Can not parse string 'Name'."); goto err; } ret = cpfl_json_t_to_uint16(object, "Value", &value); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'value'."); goto err; } if (strcmp(name, "ingress") == 0) { attr->ingress = value; } else if (strcmp(name, "egress") == 0) { attr->egress = value; } else { /* TODO: more... */ PMD_DRV_LOG(ERR, "Not support attr name: %s.", name); goto err; } } return 0; err: rte_free(js_pr->key.attributes); return -EINVAL; } static int cpfl_flow_js_pattern_key_proto_field(json_t *ob_fields, struct cpfl_flow_js_pr_key_proto *js_field) { int len, i; if (!ob_fields) return 0; len = json_array_size(ob_fields); if (len == 0) return 0; js_field->fields_size = len; js_field->fields = rte_malloc(NULL, sizeof(struct cpfl_flow_js_pr_key_proto_field) * len, 0); if (!js_field->fields) { PMD_DRV_LOG(ERR, "Failed to alloc memory."); return -ENOMEM; } for (i = 0; i < len; i++) { json_t *object; const char *name, *mask; uint32_t mask_32b = 0; int ret; object = json_array_get(ob_fields, i); name = cpfl_json_t_to_string(object, "name"); if (!name) { PMD_DRV_LOG(ERR, "Can not parse string 'name'."); goto err; } if (strlen(name) > CPFL_JS_STR_SIZE - 1) { PMD_DRV_LOG(ERR, "The 'name' is too long."); goto err; } strlcpy(js_field->fields[i].name, name, CPFL_JS_STR_SIZE); if (js_field->type == RTE_FLOW_ITEM_TYPE_ETH || js_field->type == RTE_FLOW_ITEM_TYPE_IPV4) { /* Added a check for parsing mask value of the next_proto_id field. */ if (strcmp(name, "next_proto_id") == 0) { ret = cpfl_json_t_to_uint32(object, "mask", &mask_32b); if (ret < 0) { PMD_DRV_LOG(ERR, "Cannot parse uint32 'mask'."); goto err; } js_field->fields[i].mask_32b = mask_32b; } else { mask = cpfl_json_t_to_string(object, "mask"); if (!mask) { PMD_DRV_LOG(ERR, "Can not parse string 'mask'."); goto err; } if (rte_strscpy(js_field->fields[i].mask, mask, CPFL_JS_STR_SIZE) < 0) { PMD_DRV_LOG(ERR, "The 'mask' is too long."); goto err; } } } else { ret = cpfl_json_t_to_uint32(object, "mask", &mask_32b); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse uint32 'mask'."); goto err; } js_field->fields[i].mask_32b = mask_32b; } } return 0; err: rte_free(js_field->fields); return -EINVAL; } static int cpfl_flow_js_pattern_key_proto(json_t *ob_pr_key_protos, struct cpfl_flow_js_pr *js_pr) { int len, i, ret; len = json_array_size(ob_pr_key_protos); if (len == 0) return 0; js_pr->key.proto_size = len; js_pr->key.protocols = rte_malloc(NULL, sizeof(struct cpfl_flow_js_pr_key_proto) * len, 0); if (!js_pr->key.protocols) { PMD_DRV_LOG(ERR, "Failed to alloc memory."); return -ENOMEM; } for (i = 0; i < len; i++) { json_t *object, *ob_fields; const char *type; enum rte_flow_item_type item_type; object = json_array_get(ob_pr_key_protos, i); /* pr->key->proto->type */ type = cpfl_json_t_to_string(object, "type"); if (!type) { PMD_DRV_LOG(ERR, "Can not parse string 'type'."); goto err; } item_type = cpfl_get_item_type_by_str(type); if (item_type == RTE_FLOW_ITEM_TYPE_VOID) goto err; js_pr->key.protocols[i].type = item_type; /* pr->key->proto->fields */ ob_fields = json_object_get(object, "fields"); ret = cpfl_flow_js_pattern_key_proto_field(ob_fields, &js_pr->key.protocols[i]); if (ret < 0) goto err; } return 0; err: rte_free(js_pr->key.protocols); return -EINVAL; } static int cpfl_flow_js_pattern_act_fv_proto(json_t *ob_value, struct cpfl_flow_js_fv *js_fv) { uint16_t layer = 0, offset = 0, mask = 0; const char *header; enum rte_flow_item_type type; int ret; ret = cpfl_json_t_to_uint16(ob_value, "layer", &layer); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'value'."); return -EINVAL; } header = cpfl_json_t_to_string(ob_value, "header"); if (!header) { PMD_DRV_LOG(ERR, "Can not parse string 'header'."); return -EINVAL; } ret = cpfl_json_t_to_uint16(ob_value, "offset", &offset); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'offset'."); return -EINVAL; } ret = cpfl_json_t_to_uint16(ob_value, "mask", &mask); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'mask'."); return -EINVAL; } type = cpfl_get_item_type_by_str(header); if (type == RTE_FLOW_ITEM_TYPE_VOID) return -EINVAL; js_fv->proto.layer = layer; js_fv->proto.offset = offset; js_fv->proto.mask = mask; js_fv->proto.header = type; return 0; } static int cpfl_flow_js_pattern_act_fv_metadata(json_t *ob_value, struct cpfl_flow_js_fv *js_fv) { int ret; ret = cpfl_json_t_to_uint16(ob_value, "type", &js_fv->meta.type); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'size'."); return ret; } ret = cpfl_json_t_to_uint16(ob_value, "offset", &js_fv->meta.offset); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'size'."); return ret; } ret = cpfl_json_t_to_uint16(ob_value, "mask", &js_fv->meta.mask); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'size'."); return ret; } return 0; } static int cpfl_flow_js_pattern_act_fv(json_t *ob_fvs, struct cpfl_flow_js_pr_action *js_act) { int len, i; len = json_array_size(ob_fvs); if (len == 0) return 0; js_act->sem.fv = rte_malloc(NULL, sizeof(struct cpfl_flow_js_fv) * len, 0); if (!js_act->sem.fv) { PMD_DRV_LOG(ERR, "Failed to alloc memory."); return -ENOMEM; } js_act->sem.fv_size = len; for (i = 0; i < len; i++) { struct cpfl_flow_js_fv *js_fv; json_t *object, *ob_value; uint16_t offset = 0; const char *type; int ret; js_fv = &js_act->sem.fv[i]; object = json_array_get(ob_fvs, i); ret = cpfl_json_t_to_uint16(object, "offset", &offset); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'offset'."); goto err; } js_fv->offset = offset; type = cpfl_json_t_to_string(object, "type"); if (!type) { PMD_DRV_LOG(ERR, "Can not parse string 'type'."); goto err; } ob_value = json_object_get(object, "value"); if (strcmp(type, "immediate") == 0) { js_fv->type = CPFL_FV_TYPE_IMMEDIATE; js_fv->immediate = json_integer_value(ob_value); } else if (strcmp(type, "metadata") == 0) { js_fv->type = CPFL_FV_TYPE_METADATA; cpfl_flow_js_pattern_act_fv_metadata(ob_value, js_fv); } else if (strcmp(type, "protocol") == 0) { js_fv->type = CPFL_FV_TYPE_PROTOCOL; cpfl_flow_js_pattern_act_fv_proto(ob_value, js_fv); } else { PMD_DRV_LOG(ERR, "Not support this type: %s.", type); goto err; } } return 0; err: rte_free(js_act->sem.fv); return -EINVAL; } static int cpfl_flow_js_pattern_act_fv_lem(json_t *cjson_fv, struct cpfl_flow_js_pr_action *js_act) { int len, i, ret; len = json_array_size(cjson_fv); js_act->lem.fv = rte_malloc(NULL, sizeof(struct cpfl_flow_js_fv) * len, 0); if (!js_act->lem.fv) { PMD_DRV_LOG(ERR, "Failed to alloc memory."); return -ENOMEM; } js_act->lem.fv_size = len; for (i = 0; i < len; i++) { struct cpfl_flow_js_fv *js_fv; json_t *object, *cjson_value; const char *type; object = json_array_get(cjson_fv, i); js_fv = &js_act->lem.fv[i]; ret = cpfl_json_t_to_uint16(object, "offset", &js_fv->offset); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'offset'."); return -EINVAL; } type = cpfl_json_t_to_string(object, "type"); if (!type) { PMD_DRV_LOG(ERR, "Can not parse 'type'."); return -EINVAL; } cjson_value = json_object_get(object, "value"); if (strcmp(type, "immediate") == 0) { js_fv->type = CPFL_FV_TYPE_IMMEDIATE; js_fv->immediate = json_integer_value(cjson_value); } else if (strcmp(type, "metadata") == 0) { js_fv->type = CPFL_FV_TYPE_METADATA; cpfl_flow_js_pattern_act_fv_metadata(cjson_value, js_fv); } else if (strcmp(type, "protocol") == 0) { js_fv->type = CPFL_FV_TYPE_PROTOCOL; cpfl_flow_js_pattern_act_fv_proto(cjson_value, js_fv); } else { PMD_DRV_LOG(ERR, "Not support this type: %s.", type); goto err; } } return 0; err: rte_free(js_act->lem.fv); return -EINVAL; } static int cpfl_flow_js_pattern_per_act(json_t *ob_per_act, struct cpfl_flow_js_pr_action *js_act) { const char *type; int ret; /* pr->actions->type */ type = cpfl_json_t_to_string(ob_per_act, "type"); if (!type) { PMD_DRV_LOG(ERR, "Can not parse string 'type'."); return -EINVAL; } /* pr->actions->data */ if (strcmp(type, "sem") == 0) { json_t *ob_fvs, *ob_sem; js_act->type = CPFL_JS_PR_ACTION_TYPE_SEM; ob_sem = json_object_get(ob_per_act, "data"); ret = cpfl_json_t_to_uint16(ob_sem, "profile", &js_act->sem.prof); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'profile'."); return -EINVAL; } ret = cpfl_json_t_to_uint16(ob_sem, "subprofile", &js_act->sem.subprof); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'subprofile'."); return -EINVAL; } ret = cpfl_json_t_to_uint16(ob_sem, "keysize", &js_act->sem.keysize); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'keysize'."); return -EINVAL; } ob_fvs = json_object_get(ob_sem, "fieldvectors"); ret = cpfl_flow_js_pattern_act_fv(ob_fvs, js_act); if (ret < 0) return ret; } else if (strcmp(type, "lem") == 0) { js_act->type = CPFL_JS_PR_ACTION_TYPE_LEM; json_t *cjson_fv, *ob_lem; ob_lem = json_object_get(ob_per_act, "data"); ret = cpfl_json_t_to_uint16(ob_lem, "profile", &js_act->lem.prof); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'profile'."); return -EINVAL; } ret = cpfl_json_t_to_uint16(ob_lem, "keysize", &js_act->lem.keysize); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'keysize'."); return -EINVAL; } cjson_fv = json_object_get(ob_lem, "fieldvectors"); ret = cpfl_flow_js_pattern_act_fv_lem(cjson_fv, js_act); if (ret < 0) return ret; } else { PMD_DRV_LOG(ERR, "Not support this type: %s.", type); return -EINVAL; } return 0; } static int cpfl_flow_js_pattern_act(json_t *ob_pr_acts, struct cpfl_flow_js_pr *js_pr) { int i, len, ret; len = json_array_size(ob_pr_acts); if (len == 0) return 0; js_pr->actions = rte_malloc(NULL, sizeof(struct cpfl_flow_js_pr_action) * len, 0); if (!js_pr->actions) { PMD_DRV_LOG(ERR, "Failed to alloc memory."); return -ENOMEM; } js_pr->actions_size = len; for (i = 0; i < len; i++) { struct cpfl_flow_js_pr_action *js_act; json_t *object; object = json_array_get(ob_pr_acts, i); js_act = &js_pr->actions[i]; ret = cpfl_flow_js_pattern_per_act(object, js_act); if (ret < 0) { rte_free(js_pr->actions); PMD_DRV_LOG(ERR, "Can not parse pattern action."); return -EINVAL; } } return 0; } /** * The patterns object array defines a set of rules directing the PMD to match sequences of * rte_flow protocol headers and translate them into profile/field vectors for each pipeline * stage. This object is mandatory. */ static int cpfl_flow_js_pattern_rule(json_t *ob_root, struct cpfl_flow_js_parser *parser) { json_t *ob_prs; int i, len; /* Pattern Rules */ ob_prs = json_object_get(ob_root, "patterns"); if (!ob_prs) { PMD_DRV_LOG(ERR, "The patterns is mandatory."); return -EINVAL; } len = json_array_size(ob_prs); if (len == 0) return 0; parser->patterns = rte_malloc(NULL, sizeof(struct cpfl_flow_js_pr) * len, 0); if (!parser->patterns) { PMD_DRV_LOG(ERR, "Failed to alloc memory."); return -ENOMEM; } parser->pr_size = len; for (i = 0; i < len; i++) { json_t *object; json_t *ob_pr_actions; json_t *ob_pr_key; json_t *ob_pr_key_protos; json_t *ob_pr_key_attrs; int ret; object = json_array_get(ob_prs, i); /* pr->key */ ob_pr_key = json_object_get(object, "key"); /* pr->key->protocols */ ob_pr_key_protos = json_object_get(ob_pr_key, "protocols"); ret = cpfl_flow_js_pattern_key_proto(ob_pr_key_protos, &parser->patterns[i]); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse key->protocols."); goto err; } /* pr->key->attributes */ ob_pr_key_attrs = json_object_get(ob_pr_key, "attributes"); ret = cpfl_flow_js_pattern_key_attr(ob_pr_key_attrs, &parser->patterns[i]); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse key->attributes."); goto err; } /* pr->actions */ ob_pr_actions = json_object_get(object, "actions"); ret = cpfl_flow_js_pattern_act(ob_pr_actions, &parser->patterns[i]); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse pattern action."); goto err; } } return 0; err: rte_free(parser->patterns); return -EINVAL; } static int cpfl_flow_js_mr_key(json_t *ob_mr_keys, struct cpfl_flow_js_mr_key *js_mr_key) { int len, i; len = json_array_size(ob_mr_keys); if (len == 0) return 0; js_mr_key->actions = rte_malloc(NULL, sizeof(struct cpfl_flow_js_mr_key_action) * len, 0); if (!js_mr_key->actions) { PMD_DRV_LOG(ERR, "Failed to alloc memory."); return -ENOMEM; } js_mr_key->actions_size = len; for (i = 0; i < len; i++) { json_t *object, *ob_data; const char *type; enum rte_flow_action_type act_type; object = json_array_get(ob_mr_keys, i); /* mr->key->actions->type */ type = cpfl_json_t_to_string(object, "type"); if (!type) { PMD_DRV_LOG(ERR, "Can not parse string 'type'."); goto err; } act_type = cpfl_get_action_type_by_str(type); if (act_type == RTE_FLOW_ACTION_TYPE_VOID) goto err; js_mr_key->actions[i].type = act_type; /* mr->key->actions->data */ ob_data = json_object_get(object, "data"); if (js_mr_key->actions[i].type == RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP) { json_t *ob_protos; int proto_size, j; struct cpfl_flow_js_mr_key_action_vxlan_encap *encap; ob_protos = json_object_get(ob_data, "protocols"); encap = &js_mr_key->actions[i].encap; if (!ob_protos) { encap->proto_size = 0; continue; } proto_size = json_array_size(ob_protos); encap->proto_size = proto_size; for (j = 0; j < proto_size; j++) { const char *s; json_t *subobject; enum rte_flow_item_type proto_type; subobject = json_array_get(ob_protos, j); s = json_string_value(subobject); proto_type = cpfl_get_item_type_by_str(s); if (proto_type == RTE_FLOW_ITEM_TYPE_VOID) { PMD_DRV_LOG(ERR, "parse VXLAN_ENCAP failed."); goto err; } encap->protocols[j] = proto_type; } } else if (js_mr_key->actions[i].type == RTE_FLOW_ACTION_TYPE_PROG) { int ret; uint32_t param_size, j; uint16_t value = 0; json_t *ob_param, *subobject; const char *name; ret = cpfl_json_t_to_uint32(object, "id", &js_mr_key->actions[i].prog.id); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse uint32 'id'."); goto err; } if (json_object_get(object, "name")) { js_mr_key->actions[i].prog.has_name = TRUE; name = cpfl_json_t_to_string(object, "name"); if (!name) { PMD_DRV_LOG(ERR, "Can not parse string 'name'."); goto err; } if (strlen(name) > CPFL_JS_STR_SIZE - 1) { PMD_DRV_LOG(ERR, "The 'name' is too long."); goto err; } strlcpy(js_mr_key->actions[i].prog.name, name, CPFL_JS_STR_SIZE); } ob_param = json_object_get(object, "parameters"); param_size = json_array_size(ob_param); js_mr_key->actions[i].prog.param_size = param_size; for (j = 0; j < param_size; j++) { subobject = json_array_get(ob_param, j); ret = cpfl_json_t_to_uint16(subobject, "index", &value); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'index'."); goto err; } js_mr_key->actions[i].prog.params[j].index = value; if (json_object_get(subobject, "name")) { js_mr_key->actions[i].prog.params[j].has_name = TRUE; name = cpfl_json_t_to_string(subobject, "name"); if (!name) { PMD_DRV_LOG(ERR, "Can not parse string 'name'."); goto err; } if (strlen(name) > CPFL_JS_STR_SIZE - 1) { PMD_DRV_LOG(ERR, "The 'name' is too long."); goto err; } strlcpy(js_mr_key->actions[i].prog.params[j].name, name, CPFL_JS_STR_SIZE); } ret = cpfl_json_t_to_uint16(subobject, "size", &value); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'size'."); goto err; } js_mr_key->actions[i].prog.params[j].size = value; } } else if (js_mr_key->actions[i].type != RTE_FLOW_ACTION_TYPE_VXLAN_DECAP) { PMD_DRV_LOG(ERR, "not support this type: %d.", js_mr_key->actions[i].type); goto err; } } return 0; err: rte_free(js_mr_key->actions); return -EINVAL; } static int cpfl_flow_js_mr_layout(json_t *ob_layouts, struct cpfl_flow_js_mr_action_mod *js_mod) { int len, i; len = json_array_size(ob_layouts); js_mod->layout_size = len; if (len == 0) return 0; for (i = 0; i < len; i++) { json_t *object; int index = 0, size = 0, offset = 0; int ret; const char *hint; object = json_array_get(ob_layouts, i); ret = cpfl_json_t_to_int(object, "index", &index); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'index'."); goto err; } js_mod->layout[i].index = index; ret = cpfl_json_t_to_int(object, "size", &size); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'size'."); goto err; } js_mod->layout[i].size = size; ret = cpfl_json_t_to_int(object, "offset", &offset); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'offset'."); goto err; } js_mod->layout[i].offset = offset; hint = cpfl_json_t_to_string(object, "hint"); if (!hint) { PMD_DRV_LOG(ERR, "Can not parse string 'hint'."); goto err; } if (strlen(hint) > CPFL_JS_STR_SIZE - 1) { PMD_DRV_LOG(ERR, "The 'hint' is too long."); goto err; } strlcpy(js_mod->layout[i].hint, hint, CPFL_JS_STR_SIZE); } return 0; err: return -EINVAL; } static int cpfl_flow_js_mr_content(json_t *ob_content, struct cpfl_flow_js_mr_action_mod *js_mod) { int ret, len, i; json_t *ob_field; if (!ob_content) return 0; js_mod->is_content = TRUE; ret = cpfl_json_t_to_uint16(ob_content, "size", &js_mod->content.size); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'size'."); return -EINVAL; } ob_field = json_object_get(ob_content, "fields"); len = json_array_size(ob_field); js_mod->content.field_size = len; if (len == 0) return 0; for (i = 0; i < len; i++) { json_t *object; uint16_t start = 0, width = 0, index = 0; const char *type; object = json_array_get(ob_field, i); type = cpfl_json_t_to_string(object, "type"); if (!type) { PMD_DRV_LOG(ERR, "Can not parse string 'type'."); goto err; } if (strlen(type) > CPFL_JS_STR_SIZE - 1) { PMD_DRV_LOG(ERR, "The 'type' is too long."); goto err; } strlcpy(js_mod->content.fields[i].type, type, CPFL_JS_STR_SIZE); ret = cpfl_json_t_to_uint16(object, "start", &start); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'start'."); goto err; } js_mod->content.fields[i].start = start; ret = cpfl_json_t_to_uint16(object, "width", &width); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'width'."); goto err; } js_mod->content.fields[i].width = width; if (strcmp(type, "parameter") == 0) { ret = cpfl_json_t_to_uint16(object, "index", &index); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'index'."); goto err; } js_mod->content.fields[i].index = index; } else if (strcmp(type, "constant") == 0) { json_t *ob_value, *subobj; int value_len, j; ob_value = json_object_get(object, "value"); value_len = json_array_size(ob_value); for (j = 0; j < value_len; j++) { subobj = json_array_get(ob_value, j); js_mod->content.fields[i].value[j] = (uint8_t)json_integer_value(subobj); } } } return 0; err: return -EINVAL; } static int cpfl_flow_js_mr_action(json_t *ob_mr_act, struct cpfl_flow_js_mr_action *js_mr_act) { json_t *ob_data; const char *type; /* mr->action->type */ type = cpfl_json_t_to_string(ob_mr_act, "type"); if (!type) { PMD_DRV_LOG(ERR, "Can not parse string 'type'."); return -EINVAL; } /* mr->action->data */ ob_data = json_object_get(ob_mr_act, "data"); if (strcmp(type, "mod") == 0) { json_t *ob_layouts, *ob_content; uint16_t profile = 0; int ret; js_mr_act->type = CPFL_JS_MR_ACTION_TYPE_MOD; ret = cpfl_json_t_to_uint16(ob_data, "profile", &profile); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'profile'."); return -EINVAL; } js_mr_act->mod.prof = profile; ob_layouts = json_object_get(ob_data, "layout"); ret = cpfl_flow_js_mr_layout(ob_layouts, &js_mr_act->mod); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse layout."); return ret; } ob_content = json_object_get(ob_data, "content"); ret = cpfl_flow_js_mr_content(ob_content, &js_mr_act->mod); if (ret < 0) { PMD_DRV_LOG(ERR, "Can not parse 'content'."); return ret; } } else { PMD_DRV_LOG(ERR, "not support this type: %s.", type); return -EINVAL; } return 0; } /** * The modifications object array defines a set of rules for the PMD to match rte_flow * modification actions and translate them into the Modification profile. This object * is optional. */ static int cpfl_flow_js_mod_rule(json_t *ob_root, struct cpfl_flow_js_parser *parser) { json_t *ob_mrs; int i, len; ob_mrs = json_object_get(ob_root, "modifications"); if (!ob_mrs) { PMD_DRV_LOG(INFO, "The modifications is optional."); return 0; } len = json_array_size(ob_mrs); if (len == 0) return 0; parser->mr_size = len; parser->modifications = rte_malloc(NULL, sizeof(struct cpfl_flow_js_mr) * len, 0); if (!parser->modifications) { PMD_DRV_LOG(ERR, "Failed to alloc memory."); return -ENOMEM; } for (i = 0; i < len; i++) { int ret; json_t *object, *ob_mr_key, *ob_mr_action, *ob_mr_key_action; object = json_array_get(ob_mrs, i); /* mr->key */ ob_mr_key = json_object_get(object, "key"); /* mr->key->actions */ ob_mr_key_action = json_object_get(ob_mr_key, "actions"); ret = cpfl_flow_js_mr_key(ob_mr_key_action, &parser->modifications[i].key); if (ret < 0) { PMD_DRV_LOG(ERR, "parse mr_key failed."); goto err; } /* mr->action */ ob_mr_action = json_object_get(object, "action"); ret = cpfl_flow_js_mr_action(ob_mr_action, &parser->modifications[i].action); if (ret < 0) { PMD_DRV_LOG(ERR, "parse mr_action failed."); goto err; } } return 0; err: rte_free(parser->modifications); return -EINVAL; } static int cpfl_parser_init(json_t *ob_root, struct cpfl_flow_js_parser *parser) { int ret = 0; ret = cpfl_flow_js_pattern_rule(ob_root, parser); if (ret < 0) { PMD_DRV_LOG(ERR, "parse pattern_rule failed."); return ret; } ret = cpfl_flow_js_mod_rule(ob_root, parser); if (ret < 0) { PMD_DRV_LOG(ERR, "parse mod_rule failed."); return ret; } return 0; } int cpfl_parser_create(struct cpfl_flow_js_parser **flow_parser, const char *filename) { struct cpfl_flow_js_parser *parser; json_error_t json_error; json_t *root; int ret; parser = rte_zmalloc("flow_parser", sizeof(struct cpfl_flow_js_parser), 0); if (!parser) { PMD_DRV_LOG(ERR, "Not enough memory to create flow parser."); return -ENOMEM; } root = json_load_file(filename, 0, &json_error); if (!root) { PMD_DRV_LOG(ERR, "Bad JSON file \"%s\": %s", filename, json_error.text); goto free_parser; } ret = cpfl_parser_init(root, parser); if (ret < 0) { PMD_DRV_LOG(ERR, "parser init failed."); goto free_parser; } *flow_parser = parser; json_decref(root); return 0; free_parser: rte_free(parser); return -EINVAL; } static void cpfl_parser_free_pr_action(struct cpfl_flow_js_pr_action *pr_act) { if (pr_act->type == CPFL_JS_PR_ACTION_TYPE_SEM) rte_free(pr_act->sem.fv); else if (pr_act->type == CPFL_JS_PR_ACTION_TYPE_LEM) rte_free(pr_act->lem.fv); } int cpfl_parser_destroy(struct cpfl_flow_js_parser *parser) { int i, j; if (!parser) return 0; for (i = 0; i < parser->pr_size; i++) { struct cpfl_flow_js_pr *pattern = &parser->patterns[i]; for (j = 0; j < pattern->key.proto_size; j++) rte_free(pattern->key.protocols[j].fields); rte_free(pattern->key.protocols); rte_free(pattern->key.attributes); for (j = 0; j < pattern->actions_size; j++) { struct cpfl_flow_js_pr_action *pr_act; pr_act = &pattern->actions[j]; cpfl_parser_free_pr_action(pr_act); } rte_free(pattern->actions); } rte_free(parser->patterns); for (i = 0; i < parser->mr_size; i++) { struct cpfl_flow_js_mr *mr = &parser->modifications[i]; rte_free(mr->key.actions); } rte_free(parser->modifications); rte_free(parser); return 0; } static int cpfl_get_items_length(const struct rte_flow_item *items) { int length = 0; const struct rte_flow_item *item = items; while ((item + length++)->type != RTE_FLOW_ITEM_TYPE_END) continue; return length; } static int cpfl_get_actions_length(const struct rte_flow_action *actions) { int length = 0; const struct rte_flow_action *action = actions; while ((action + length++)->type != RTE_FLOW_ACTION_TYPE_END) continue; return length; } static int cpfl_parse_fv_protocol(struct cpfl_flow_js_fv *js_fv, const struct rte_flow_item *items, uint16_t offset, uint8_t *fv) { uint16_t v_layer, v_offset, v_mask; enum rte_flow_item_type v_header; int j, layer, length; uint16_t temp_fv; length = cpfl_get_items_length(items); v_layer = js_fv->proto.layer; v_header = js_fv->proto.header; v_offset = js_fv->proto.offset; v_mask = js_fv->proto.mask; layer = 0; for (j = 0; j < length - 1; j++) { if (items[j].type == v_header) { if (layer == v_layer) { /* copy out 16 bits from offset */ const uint8_t *pointer; pointer = &(((const uint8_t *)(items[j].spec))[v_offset]); temp_fv = ntohs((*((const uint16_t *)pointer)) & v_mask); fv[2 * offset] = (uint8_t)(temp_fv >> 8); fv[2 * offset + 1] = (uint8_t)(temp_fv & 0x00ff); break; } layer++; } } return 0; } static int cpfl_parse_fieldvectors(struct cpfl_itf *itf, struct cpfl_flow_js_fv *js_fvs, int size, uint8_t *fv, const struct rte_flow_item *items) { int i, ret; for (i = 0; i < size; i++) { uint16_t offset, temp_fv, value_int; enum cpfl_flow_js_fv_type type; struct cpfl_flow_js_fv *js_fv; js_fv = &js_fvs[i]; offset = js_fv->offset; type = js_fv->type; if (type == CPFL_FV_TYPE_IMMEDIATE) { value_int = js_fv->immediate; temp_fv = (value_int << 8) & 0xff00; fv[2 * offset] = (uint8_t)(temp_fv >> 8); fv[2 * offset + 1] = (uint8_t)(temp_fv & 0x00ff); } else if (type == CPFL_FV_TYPE_METADATA) { uint16_t v_offset, mask; v_offset = js_fv->meta.offset; mask = js_fv->meta.mask; temp_fv = cpfl_metadata_read16(&itf->adapter->meta, js_fv->meta.type, v_offset) & mask; fv[2 * offset] = (uint8_t)(temp_fv & 0x00ff); fv[2 * offset + 1] = (uint8_t)(temp_fv >> 8); } else if (type == CPFL_FV_TYPE_PROTOCOL) { ret = cpfl_parse_fv_protocol(js_fv, items, offset, fv); if (ret) return ret; } else { PMD_DRV_LOG(DEBUG, "not support this type: %d.", type); return -EINVAL; } } return 0; } static int cpfl_parse_pr_actions(struct cpfl_itf *itf, struct cpfl_flow_js_pr_action *actions, int size, const struct rte_flow_item *items, const struct rte_flow_attr *attr, struct cpfl_flow_pr_action *pr_action) { int i, ret; for (i = 0; i < size; i++) { struct cpfl_flow_js_pr_action *pr_act; enum cpfl_flow_pr_action_type type; pr_act = &actions[i]; /* pr->actions->type */ type = pr_act->type; /* pr->actions->data */ if (attr->group == 1 && type == CPFL_JS_PR_ACTION_TYPE_SEM) { struct cpfl_flow_js_pr_action_sem *sem = &pr_act->sem; pr_action->type = CPFL_JS_PR_ACTION_TYPE_SEM; pr_action->sem.prof = sem->prof; pr_action->sem.subprof = sem->subprof; pr_action->sem.keysize = sem->keysize; memset(pr_action->sem.cpfl_flow_pr_fv, 0, sizeof(pr_action->sem.cpfl_flow_pr_fv)); ret = cpfl_parse_fieldvectors(itf, sem->fv, sem->fv_size, pr_action->sem.cpfl_flow_pr_fv, items); return ret; } else if (attr->group % 10 == 4 && type == CPFL_JS_PR_ACTION_TYPE_LEM) { struct cpfl_flow_js_pr_action_lem *lem = &pr_act->lem; pr_action->type = CPFL_JS_PR_ACTION_TYPE_LEM; pr_action->lem.prof = lem->prof; pr_action->lem.keysize = lem->keysize; memset(pr_action->lem.cpfl_flow_pr_fv, 0, sizeof(pr_action->lem.cpfl_flow_pr_fv)); ret = cpfl_parse_fieldvectors(itf, lem->fv, lem->fv_size, pr_action->lem.cpfl_flow_pr_fv, items); if (ret < 0) return ret; continue; } else if (attr->group > 4 || attr->group == 0) { return -EPERM; } } return 0; } static int cpfl_check_eth_mask(const char *mask, struct rte_ether_addr addr) { int i, ret; struct rte_ether_addr mask_bytes; ret = rte_ether_unformat_addr(mask, &mask_bytes); if (ret < 0) { PMD_DRV_LOG(ERR, "translate mac address from string to rte_ether_addr failed."); return -EINVAL; } /* validate eth mask addr if match */ for (i = 0; i < RTE_ETHER_ADDR_LEN; i++) { if (mask_bytes.addr_bytes[i] != addr.addr_bytes[i]) return -EINVAL; } return 0; } static int cpfl_check_ipv4_mask(const char *mask, rte_be32_t addr) { uint32_t out_addr; /* 0: success; -EINVAL: invalid; -ENOTSUP: fail */ int ret = inet_pton(AF_INET, mask, &out_addr); if (ret < 0) return -EINVAL; /* validate ipv4 mask addr if match */ if (out_addr != addr) return -EINVAL; return 0; } static int cpfl_check_eth(struct cpfl_flow_js_pr_key_proto *proto, const struct rte_flow_item_eth *eth_mask) { int field_size, j; int flag_dst_addr, flag_src_addr, flag_ether_type; struct cpfl_flow_js_pr_key_proto_field *field; if (!proto) return 0; field_size = proto->fields_size; if (field_size != 0 && !eth_mask) return -EINVAL; if (field_size == 0 && eth_mask) return -EINVAL; if (field_size == 0 && !eth_mask) return 0; flag_dst_addr = false; flag_src_addr = false; flag_ether_type = false; for (j = 0; j < field_size; j++) { const char *name, *s_mask; field = &proto->fields[j]; /* match: rte_flow_item_eth.dst, more see Field Mapping */ name = field->name; /* match: rte_flow_item->mask */ if (strcmp(name, "src_addr") == 0) { s_mask = field->mask; if (cpfl_check_eth_mask(s_mask, eth_mask->src) < 0) return -EINVAL; flag_src_addr = true; } else if (strcmp(name, "dst_addr") == 0) { s_mask = field->mask; if (cpfl_check_eth_mask(s_mask, eth_mask->dst) < 0) return -EINVAL; flag_dst_addr = true; } else if (strcmp(name, "ether_type") == 0) { uint16_t mask = (uint16_t)field->mask_32b; if (mask != eth_mask->type) return -EINVAL; flag_ether_type = true; } else { /* TODO: more type... */ PMD_DRV_LOG(ERR, "not support this name."); return -EINVAL; } } if (!flag_src_addr) { if (strcmp((const char *)eth_mask->src.addr_bytes, "\x00\x00\x00\x00\x00\x00") != 0) return -EINVAL; } if (!flag_dst_addr) { if (strcmp((const char *)eth_mask->dst.addr_bytes, "\x00\x00\x00\x00\x00\x00") != 0) return -EINVAL; } if (!flag_ether_type) { if (eth_mask->hdr.ether_type != (rte_be16_t)0) return -EINVAL; } return 0; } static int cpfl_check_ipv4(struct cpfl_flow_js_pr_key_proto *proto, const struct rte_flow_item_ipv4 *ipv4_mask) { int field_size, j; int flag_next_proto_id, flag_src_addr, flag_dst_addr; struct cpfl_flow_js_pr_key_proto_field *field; if (!proto) return 0; field_size = proto->fields_size; if (field_size != 0 && !ipv4_mask) return -EINVAL; if (field_size == 0 && ipv4_mask) return -EINVAL; if (field_size == 0 && !ipv4_mask) return 0; flag_dst_addr = false; flag_src_addr = false; flag_next_proto_id = false; for (j = 0; j < field_size; j++) { const char *name; field = &proto->fields[j]; name = field->name; if (strcmp(name, "src_addr") == 0) { const char *mask; mask = field->mask; if (cpfl_check_ipv4_mask(mask, ipv4_mask->hdr.src_addr) < 0) return -EINVAL; flag_src_addr = true; } else if (strcmp(name, "dst_addr") == 0) { const char *mask; mask = field->mask; if (cpfl_check_ipv4_mask(mask, ipv4_mask->hdr.dst_addr) < 0) return -EINVAL; flag_dst_addr = true; } else if (strcmp(name, "next_proto_id") == 0) { uint8_t mask; mask = (uint8_t)field->mask_32b; if (mask != ipv4_mask->hdr.next_proto_id) return -EINVAL; flag_next_proto_id = true; } else { PMD_DRV_LOG(ERR, "not support this name."); return -EINVAL; } } if (!flag_src_addr) { if (ipv4_mask->hdr.src_addr != (rte_be32_t)0) return -EINVAL; } if (!flag_dst_addr) { if (ipv4_mask->hdr.dst_addr != (rte_be32_t)0) return -EINVAL; } if (!flag_next_proto_id) { if (ipv4_mask->hdr.next_proto_id != (uint8_t)0) return -EINVAL; } return 0; } static int cpfl_check_tcp(struct cpfl_flow_js_pr_key_proto *proto, const struct rte_flow_item_tcp *tcp_mask) { int field_size, j; int flag_src_port, flag_dst_port; struct cpfl_flow_js_pr_key_proto_field *field; if (!proto) return 0; field_size = proto->fields_size; if (field_size != 0 && !tcp_mask) return -EINVAL; if (field_size == 0 && tcp_mask) return -EINVAL; if (field_size == 0 && !tcp_mask) return 0; flag_src_port = false; flag_dst_port = false; for (j = 0; j < field_size; j++) { const char *name; uint16_t mask; field = &proto->fields[j]; name = field->name; mask = (uint16_t)field->mask_32b; if (strcmp(name, "src_port") == 0) { if (tcp_mask->hdr.src_port != mask) return -EINVAL; flag_src_port = true; } else if (strcmp(name, "dst_port") == 0) { if (tcp_mask->hdr.dst_port != mask) return -EINVAL; flag_dst_port = true; } else { PMD_DRV_LOG(ERR, "not support this name."); return -EINVAL; } } if (!flag_src_port) { if (tcp_mask->hdr.src_port != (rte_be16_t)0) return -EINVAL; } if (!flag_dst_port) { if (tcp_mask->hdr.dst_port != (rte_be16_t)0) return -EINVAL; } return 0; } static int cpfl_check_udp(struct cpfl_flow_js_pr_key_proto *proto, const struct rte_flow_item_udp *udp_mask) { int field_size, j; bool flag_src_port, flag_dst_port; struct cpfl_flow_js_pr_key_proto_field *field; if (!proto) return 0; field_size = proto->fields_size; if (field_size != 0 && !udp_mask) return -EINVAL; if (field_size == 0 && udp_mask) return -EINVAL; if (field_size == 0 && !udp_mask) return 0; flag_src_port = false; flag_dst_port = false; for (j = 0; j < field_size; j++) { const char *name; uint16_t mask; field = &proto->fields[j]; /* match: rte_flow_item_eth.dst */ name = field->name; /* match: rte_flow_item->mask */ mask = (uint16_t)field->mask_32b; if (strcmp(name, "src_port") == 0) { if (udp_mask->hdr.src_port != mask) return -EINVAL; flag_src_port = true; } else if (strcmp(name, "dst_port") == 0) { if (udp_mask->hdr.dst_port != mask) return -EINVAL; flag_dst_port = true; } else { PMD_DRV_LOG(ERR, "not support this name: %s.", name); return -EINVAL; } } if (!flag_src_port) { if (udp_mask->hdr.src_port != (rte_be16_t)0) return -EINVAL; } if (!flag_dst_port) { if (udp_mask->hdr.dst_port != (rte_be16_t)0) return -EINVAL; } return 0; } static int cpfl_check_vxlan(struct cpfl_flow_js_pr_key_proto *proto, const struct rte_flow_item_vxlan *vxlan_mask) { int field_size, j; struct cpfl_flow_js_pr_key_proto_field *field; if (!proto) return 0; field_size = proto->fields_size; if (field_size != 0 && !vxlan_mask) return -EINVAL; if (field_size == 0 && vxlan_mask) return -EINVAL; if (field_size == 0 && !vxlan_mask) return 0; for (j = 0; j < field_size; j++) { const char *name; int64_t mask; field = &proto->fields[j]; name = field->name; /* match: rte_flow_item->mask */ mask = (int64_t)field->mask_32b; if (strcmp(name, "vx_vni") == 0) { if ((int64_t)RTE_BE32(vxlan_mask->hdr.vx_vni) != mask) return -EINVAL; } else { PMD_DRV_LOG(ERR, "not support this name."); return -EINVAL; } } return 0; } static int cpfl_check_icmp(struct cpfl_flow_js_pr_key_proto *proto, const struct rte_flow_item_icmp *icmp_mask) { int field_size; if (!proto) return 0; field_size = proto->fields_size; if ((field_size != 0 && !icmp_mask) || (field_size == 0 && icmp_mask)) return -EINVAL; return 0; } static int cpfl_check_pattern_key_proto(struct cpfl_flow_js_pr_key_proto *protocols, int proto_size, const struct rte_flow_item *items) { int i, length; int j = 0; length = cpfl_get_items_length(items); if (proto_size > length - 1) return -EINVAL; for (i = 0; i < proto_size; i++) { struct cpfl_flow_js_pr_key_proto *key_proto; enum rte_flow_item_type type; key_proto = &protocols[i]; /* pr->key->proto->type */ type = key_proto->type; /* pr->key->proto->fields */ switch (type) { case RTE_FLOW_ITEM_TYPE_ETH: if (items[j++].type == RTE_FLOW_ITEM_TYPE_ETH) { const struct rte_flow_item_eth *eth_mask; int ret; eth_mask = (const struct rte_flow_item_eth *)items[i].mask; ret = cpfl_check_eth(key_proto, eth_mask); if (ret < 0) return ret; } else { return -EINVAL; } break; case RTE_FLOW_ITEM_TYPE_IPV4: if (items[j++].type == RTE_FLOW_ITEM_TYPE_IPV4) { const struct rte_flow_item_ipv4 *ipv4_mask; int ret; ipv4_mask = (const struct rte_flow_item_ipv4 *)items[i].mask; ret = cpfl_check_ipv4(key_proto, ipv4_mask); if (ret < 0) return ret; } else { return -EINVAL; } break; case RTE_FLOW_ITEM_TYPE_TCP: if (items[j++].type == RTE_FLOW_ITEM_TYPE_TCP) { const struct rte_flow_item_tcp *tcp_mask; int ret; tcp_mask = (const struct rte_flow_item_tcp *)items[i].mask; ret = cpfl_check_tcp(key_proto, tcp_mask); if (ret < 0) return ret; } else { return -EINVAL; } break; case RTE_FLOW_ITEM_TYPE_UDP: if (items[j++].type == RTE_FLOW_ITEM_TYPE_UDP) { const struct rte_flow_item_udp *udp_mask; int ret; udp_mask = (const struct rte_flow_item_udp *)items[i].mask; ret = cpfl_check_udp(key_proto, udp_mask); if (ret < 0) return ret; } else { return -EINVAL; } break; case RTE_FLOW_ITEM_TYPE_VXLAN: if (items[j++].type == RTE_FLOW_ITEM_TYPE_VXLAN) { const struct rte_flow_item_vxlan *vxlan_mask; int ret; vxlan_mask = (const struct rte_flow_item_vxlan *)items[i].mask; ret = cpfl_check_vxlan(key_proto, vxlan_mask); if (ret < 0) return ret; } else { return -EINVAL; } break; case RTE_FLOW_ITEM_TYPE_ICMP: if (items[j++].type == RTE_FLOW_ITEM_TYPE_ICMP) { const struct rte_flow_item_icmp *icmp_mask; int ret; icmp_mask = (const struct rte_flow_item_icmp *)items[i].mask; ret = cpfl_check_icmp(key_proto, icmp_mask); if (ret < 0) return ret; } else { return -EINVAL; } break; default: PMD_DRV_LOG(ERR, "Not support this type: %d.", type); return -EPERM; } } if (items[j].type != RTE_FLOW_ITEM_TYPE_END) return -EINVAL; return 0; } static int cpfl_check_pattern_key_attr(struct cpfl_flow_js_pr_key_attr *key_attr, const struct rte_flow_attr *attr) { if (key_attr->ingress != attr->ingress) { PMD_DRV_LOG(DEBUG, "ingress not match."); return -EINVAL; } if (key_attr->egress != attr->egress) { PMD_DRV_LOG(DEBUG, "egress not match."); return -EINVAL; } return 0; } static int cpfl_check_pattern_key(struct cpfl_flow_js_pr *pattern, const struct rte_flow_item *items, const struct rte_flow_attr *attr) { int ret; /* pr->key */ /* pr->key->protocols */ ret = cpfl_check_pattern_key_proto(pattern->key.protocols, pattern->key.proto_size, items); if (ret < 0) return -EINVAL; /* pr->key->attributes */ ret = cpfl_check_pattern_key_attr(pattern->key.attributes, attr); if (ret < 0) return -EINVAL; return 0; } /* output: struct cpfl_flow_pr_action* pr_action */ int cpfl_flow_parse_items(struct cpfl_itf *itf, struct cpfl_flow_js_parser *parser, const struct rte_flow_item *items, const struct rte_flow_attr *attr, struct cpfl_flow_pr_action *pr_action) { int i, size; struct cpfl_flow_js_pr *pattern; size = parser->pr_size; for (i = 0; i < size; i++) { int ret; pattern = &parser->patterns[i]; ret = cpfl_check_pattern_key(pattern, items, attr); if (ret < 0) continue; /* pr->actions */ ret = cpfl_parse_pr_actions(itf, pattern->actions, pattern->actions_size, items, attr, pr_action); return ret; } return -EINVAL; } /* modifications rules */ static int cpfl_check_actions_vxlan_encap(struct cpfl_flow_mr_key_action_vxlan_encap *encap, const struct rte_flow_action *action) { const struct rte_flow_action_vxlan_encap *action_vxlan_encap; struct rte_flow_item *definition; int def_length, i, proto_size; action_vxlan_encap = (const struct rte_flow_action_vxlan_encap *)action->conf; definition = action_vxlan_encap->definition; def_length = cpfl_get_items_length(definition); proto_size = encap->proto_size; if (proto_size != def_length - 1) { PMD_DRV_LOG(DEBUG, "protocols not match."); return -EINVAL; } for (i = 0; i < proto_size; i++) { enum rte_flow_item_type proto; proto = encap->protocols[i]; if (proto == RTE_FLOW_ITEM_TYPE_VLAN) { if (definition[i].type != RTE_FLOW_ITEM_TYPE_VOID) { PMD_DRV_LOG(DEBUG, "protocols not match."); return -EINVAL; } } else if (proto != definition[i].type) { PMD_DRV_LOG(DEBUG, "protocols not match."); return -EINVAL; } } return 0; } static int cpfl_parse_check_prog_action(struct cpfl_flow_js_mr_key_action *key_act, struct cpfl_flow_mr_key_action_prog *mr_key_prog, const struct rte_flow_action_prog *prog) { uint32_t k; bool check_name; check_name = key_act->prog.has_name ? strcmp(prog->name, key_act->prog.name) == 0 : (uint32_t)atol(prog->name) == key_act->prog.id; if (!check_name) { PMD_DRV_LOG(ERR, "Not support this prog type: %s.", prog->name); return -EINVAL; } if (key_act->prog.param_size != prog->args_num) return -EINVAL; for (k = 0; k < key_act->prog.param_size; k++) { const struct rte_flow_action_prog_argument *arg = &prog->args[k]; struct cpfl_flow_js_prog_parameter *param = &key_act->prog.params[k]; check_name = param->has_name ? strcmp(arg->name, param->name) == 0 : atoi(arg->name) == param->index; if (!check_name || arg->size != param->size) return -EINVAL; if (param->has_name) { mr_key_prog->has_name = TRUE; strlcpy(mr_key_prog->name[param->index], param->name, CPFL_JS_STR_SIZE); } } return 0; } /* check and parse */ static int cpfl_parse_mr_key_action(struct cpfl_flow_js_mr_key_action *key_acts, int size, const struct rte_flow_action *actions, struct cpfl_flow_mr_key_action *mr_key_action) { int actions_length, i; int j = 0; int ret; actions_length = cpfl_get_actions_length(actions); if (size > actions_length - 1) return -EINVAL; for (i = 0; i < size; i++) { enum rte_flow_action_type type; struct cpfl_flow_js_mr_key_action *key_act; key_act = &key_acts[i]; /* mr->key->actions->type */ type = key_act->type; /* mr->key->actions->data */ if (type == RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP) { int proto_size, k; struct cpfl_flow_mr_key_action_vxlan_encap *encap; while (j < actions_length && actions[j].type != RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP) { j++; } if (j >= actions_length) return -EINVAL; mr_key_action->mods[i].type = RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP; mr_key_action->mods[i].encap.action = &actions[j]; encap = &mr_key_action->mods[i].encap; proto_size = key_act->encap.proto_size; encap->proto_size = proto_size; for (k = 0; k < proto_size; k++) { enum rte_flow_item_type proto; proto = key_act->encap.protocols[k]; encap->protocols[k] = proto; } ret = cpfl_check_actions_vxlan_encap(encap, &actions[j]); if (ret < 0) return -EINVAL; j++; } else if (type == RTE_FLOW_ACTION_TYPE_VXLAN_DECAP) { while (j < actions_length && actions[j].type != RTE_FLOW_ACTION_TYPE_VXLAN_DECAP) { j++; } if (j >= actions_length) return -EINVAL; mr_key_action->mods[i].type = RTE_FLOW_ACTION_TYPE_VXLAN_DECAP; j++; } else if (type == RTE_FLOW_ACTION_TYPE_PROG) { const struct rte_flow_action_prog *prog; while (j < actions_length && actions[j].type != RTE_FLOW_ACTION_TYPE_PROG) { j++; } if (j >= actions_length) return -EINVAL; prog = actions[j].conf; mr_key_action->prog.prog = prog; ret = cpfl_parse_check_prog_action(key_act, &mr_key_action->prog, prog); if (ret < 0) return -EINVAL; } else { PMD_DRV_LOG(ERR, "Not support this type: %d.", type); return -EPERM; } } return 0; } /* output: uint8_t *buffer, uint16_t *byte_len */ static int cpfl_parse_layout(struct cpfl_flow_js_mr_layout *layouts, int layout_size, struct cpfl_flow_mr_key_mod *mods, uint8_t *buffer, uint16_t *byte_len) { int i; int start = 0; for (i = 0; i < layout_size; i++) { int index, size, offset; const char *hint; const uint8_t *addr = NULL; struct cpfl_flow_mr_key_mod *temp; struct cpfl_flow_js_mr_layout *layout; layout = &layouts[i]; /* index links to the element of the actions array. */ index = layout->index; size = layout->size; offset = layout->offset; if (index == -1) { hint = "dummpy"; start += size; continue; } hint = layout->hint; temp = mods + index; if (temp->type == RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP) { const struct rte_flow_action_vxlan_encap *action_vxlan_encap; struct rte_flow_item *definition; int def_length, k; action_vxlan_encap = (const struct rte_flow_action_vxlan_encap *)temp->encap.action->conf; definition = action_vxlan_encap->definition; def_length = cpfl_get_items_length(definition); for (k = 0; k < def_length - 1; k++) { if ((strcmp(hint, "eth") == 0 && definition[k].type == RTE_FLOW_ITEM_TYPE_ETH) || (strcmp(hint, "ipv4") == 0 && definition[k].type == RTE_FLOW_ITEM_TYPE_IPV4) || (strcmp(hint, "udp") == 0 && definition[k].type == RTE_FLOW_ITEM_TYPE_UDP) || (strcmp(hint, "tcp") == 0 && definition[k].type == RTE_FLOW_ITEM_TYPE_TCP) || (strcmp(hint, "vxlan") == 0 && definition[k].type == RTE_FLOW_ITEM_TYPE_VXLAN)) { addr = (const uint8_t *)(definition[k].spec); if (start > 255) { *byte_len = 0; PMD_DRV_LOG(ERR, "byte length is too long: %s", hint); return -EINVAL; } memcpy(buffer + start, addr + offset, size); break; } /* TODO: more hint... */ } if (k == def_length - 1) { *byte_len = 0; PMD_DRV_LOG(ERR, "can not find corresponding hint: %s", hint); return -EINVAL; } } else { *byte_len = 0; PMD_DRV_LOG(ERR, "Not support this type: %d.", temp->type); return -EINVAL; } /* else TODO: more type... */ start += size; } *byte_len = start; return 0; } static int cpfl_parse_content(struct cpfl_flow_js_mr_content *content, struct cpfl_flow_mr_key_action_prog *prog, uint8_t *buffer) { int i, j; for (i = 0; i < content->field_size; i++) { uint16_t start, width, shift_bit; start = content->fields[i].start / 8; width = (content->fields[i].width + 7) / 8; shift_bit = (8 - content->fields[i].start % 8 - content->fields[i].width % 8) % 8; for (j = 0; j < width; j++) { uint8_t old_value = 0; if (strcmp(content->fields[i].type, "parameter") == 0) { uint32_t k; uint16_t index = content->fields[i].index; const struct rte_flow_action_prog *act_prog = prog->prog; for (k = 0; k < act_prog->args_num; k++) { const char *name = act_prog->args[k].name; if ((prog->has_name && strcmp(prog->name[index], name) == 0) || (!prog->has_name && atoi(name) == index)) { old_value = act_prog->args[k].value[j]; break; } } if (k == act_prog->args_num) return -EINVAL; } else if (strcmp(content->fields[i].type, "constant") == 0) { old_value = content->fields[i].value[j]; } else { return -EINVAL; } memset(buffer + start + j, buffer[start + j] | old_value << shift_bit, 1); } } return 0; } static int cpfl_parse_mr_action(struct cpfl_flow_js_mr_action *action, struct cpfl_flow_mr_key_action *mr_key_action, struct cpfl_flow_mr_action *mr_action) { enum cpfl_flow_mr_action_type type; /* mr->action->type */ type = action->type; /* mr->action->data */ if (action->mod.is_content) { struct cpfl_flow_js_mr_content *content = &action->mod.content; mr_action->type = CPFL_JS_MR_ACTION_TYPE_MOD; mr_action->mod.byte_len = 0; mr_action->mod.prof = action->mod.prof; mr_action->mod.byte_len = content->size; memset(mr_action->mod.data, 0, sizeof(mr_action->mod.data)); return cpfl_parse_content(content, &mr_key_action->prog, mr_action->mod.data); } if (type == CPFL_JS_MR_ACTION_TYPE_MOD) { struct cpfl_flow_js_mr_layout *layout; mr_action->type = CPFL_JS_MR_ACTION_TYPE_MOD; mr_action->mod.byte_len = 0; mr_action->mod.prof = action->mod.prof; layout = action->mod.layout; if (!layout) return 0; memset(mr_action->mod.data, 0, sizeof(mr_action->mod.data)); return cpfl_parse_layout(layout, action->mod.layout_size, mr_key_action->mods, mr_action->mod.data, &mr_action->mod.byte_len); } PMD_DRV_LOG(ERR, "Not support this type: %d.", type); return -EINVAL; } static int cpfl_check_mod_key(struct cpfl_flow_js_mr *mr, const struct rte_flow_action *actions, struct cpfl_flow_mr_key_action *mr_key_action) { int key_action_size; /* mr->key->actions */ key_action_size = mr->key.actions_size; return cpfl_parse_mr_key_action(mr->key.actions, key_action_size, actions, mr_key_action); } /* output: struct cpfl_flow_mr_action *mr_action */ static int cpfl_parse_mod_rules(struct cpfl_flow_js_parser *parser, const struct rte_flow_action *actions, struct cpfl_flow_mr_action *mr_action) { int i; struct cpfl_flow_mr_key_action mr_key_action = {0}; for (i = 0; i < parser->mr_size; i++) { int ret; struct cpfl_flow_js_mr *mr; mr = &parser->modifications[i]; if (!mr) return -EINVAL; ret = cpfl_check_mod_key(mr, actions, &mr_key_action); if (ret < 0) continue; /* mr->action */ return cpfl_parse_mr_action(&mr->action, &mr_key_action, mr_action); } return -EINVAL; } int cpfl_flow_parse_actions(struct cpfl_flow_js_parser *parser, const struct rte_flow_action *actions, struct cpfl_flow_mr_action *mr_action) { /* modifications rules */ if (!parser->modifications) { PMD_DRV_LOG(INFO, "The modifications is optional."); return 0; } return cpfl_parse_mod_rules(parser, actions, mr_action); } bool cpfl_metadata_write_port_id(struct cpfl_itf *itf) { uint16_t dev_id; const int type = 0; const int offset = 5; dev_id = cpfl_get_port_id(itf); if (dev_id == CPFL_INVALID_HW_ID) { PMD_DRV_LOG(ERR, "fail to get hw ID"); return false; } cpfl_metadata_write16(&itf->adapter->meta, type, offset, dev_id << 3); return true; } bool cpfl_metadata_write_targetvsi(struct cpfl_itf *itf) { uint16_t dev_id; const int type = 6; const int offset = 2; dev_id = cpfl_get_vsi_id(itf); if (dev_id == CPFL_INVALID_HW_ID) { PMD_DRV_LOG(ERR, "fail to get hw ID"); return false; } cpfl_metadata_write16(&itf->adapter->meta, type, offset, dev_id << 1); return true; } bool cpfl_metadata_write_sourcevsi(struct cpfl_itf *itf) { uint16_t dev_id; const int type = 6; const int offset = 0; dev_id = cpfl_get_vsi_id(itf); if (dev_id == CPFL_INVALID_HW_ID) { PMD_DRV_LOG(ERR, "fail to get hw ID"); return false; } cpfl_metadata_write16(&itf->adapter->meta, type, offset, dev_id); return true; } bool cpfl_metadata_write_vsi(struct cpfl_itf *itf) { uint16_t dev_id; const int type = 0; const int offset = 24; dev_id = cpfl_get_vsi_id(itf); if (dev_id == CPFL_INVALID_HW_ID) { PMD_DRV_LOG(ERR, "fail to get hw ID"); return false; } cpfl_metadata_write16(&itf->adapter->meta, type, offset, dev_id); return true; }