#include #include /* Dump rule information. */ void classifier_rule_dump(struct rule_field_parser * rule) { char str_rule_info[2048] = {}; unsigned int max_len = sizeof(str_rule_info) - 1; int len = snprintf(str_rule_info, max_len, "Pkt classifier,rule id:%u, ", rule->rule_id); if (rule->ether_type == RTE_ETHER_TYPE_IPV4) { struct in_addr src_addr, dst_addr; src_addr.s_addr = rule->src_addr_v4; dst_addr.s_addr = rule->dst_addr_v4; len += snprintf(str_rule_info + len, max_len - len, "IPv4, "); len += snprintf(str_rule_info + len, max_len - len, "src addr: %s/%u, ", inet_ntoa(src_addr), rule->src_addr_mask_len); len += snprintf(str_rule_info + len, max_len - len, "dst addr: %s/%u, ", inet_ntoa(dst_addr), rule->dst_addr_mask_len); } else if (rule->ether_type == RTE_ETHER_TYPE_IPV6) { char str_ipv6_src[INET6_ADDRSTRLEN] = {}, str_ipv6_dst[INET6_ADDRSTRLEN] = {}; inet_ntop(AF_INET6, rule->src_addr_v6, str_ipv6_src, INET6_ADDRSTRLEN); inet_ntop(AF_INET6, rule->dst_addr_v6, str_ipv6_dst, INET6_ADDRSTRLEN); len += snprintf(str_rule_info + len, max_len - len, "IPv6, "); len += snprintf(str_rule_info + len, max_len - len, "src addr: %s/%u, ", str_ipv6_src, rule->src_addr_mask_len); len += snprintf(str_rule_info + len, max_len - len, "dst addr: %s/%u, ", str_ipv6_dst, rule->dst_addr_mask_len); } else { len += snprintf(str_rule_info + len, max_len - len, "Any, "); } if (rule->proto == IPPROTO_TCP) { len += snprintf(str_rule_info + len, max_len - len, "proto: TCP, "); } else if (rule->proto == IPPROTO_UDP) { len += snprintf(str_rule_info + len, max_len - len, "proto: UDP, "); } else { len += snprintf(str_rule_info + len, max_len - len, "proto: Any, "); } len += snprintf(str_rule_info + len, max_len - len, "src port: %u~%u, ", rule->src_port_start, rule->src_port_end); len += snprintf(str_rule_info + len, max_len - len, "dst port: %u~%u, ", rule->dst_port_start, rule->dst_port_end); len += snprintf(str_rule_info + len, max_len - len, "priority: %u, ", rule->priority); if (rule->action.type == ACTION_DROP) { len += snprintf(str_rule_info + len, max_len - len, "action: drop"); } else if (rule->action.type == ACTION_NF_STEERING) { len += snprintf(str_rule_info + len, max_len - len, "action: nf_steering, sid: %u, ", rule->action.sid); if (rule->adapter.type == ADAPTER_TYPE_EF) { len += snprintf(str_rule_info + len, max_len - len, "ef_adapter_id: %u", rule->adapter.id); } else if (rule->adapter.type == ADAPTER_TYPE_VWIRE) { len += snprintf(str_rule_info + len, max_len - len, "vwire_id: %u", rule->adapter.id); } else if (rule->adapter.type == ADAPTER_TYPE_TERA) { len += snprintf(str_rule_info + len, max_len - len, "tera_adapter_id: %u", rule->adapter.id); } else { len += snprintf(str_rule_info + len, max_len - len, "ef_adapter_id: all, vwire_id: all, tera_adapter_id: all"); } } MR_INFO("%s", str_rule_info); } /* Dump all rules information. */ void classifier_rule_list_dump(struct rule_list_parsed * rule_list) { /* Dump the total rule number */ MR_INFO(" "); MR_INFO("Pkt classifier total rules: %u", rule_list->nr_rules); MR_INFO("Pkt classifier total static rules: %u", rule_list->nr_static_rules); MR_INFO("Pkt classifier total dynamic rules: %u", rule_list->nr_dynamic_rules); for (uint16_t index = 0; index < rule_list->nr_rules; index++) { struct rule_field_parser * rule = &rule_list->rules[index]; classifier_rule_dump(rule); } } uint16_t pkt_classifier_duplicate_rules_process(struct rule_list_parsed * rule_list) { uint16_t nr_duplicate_rules = 0; /* Iterate through all rules to check for duplicates */ for (uint16_t rule_index = 0; rule_index < rule_list->nr_rules; rule_index++) { struct rule_field_parser * rule = &rule_list->rules[rule_index]; /* Iterate through all rules to compare with the current rule */ rule->rule_state = RULE_STATE_ACTIVE; for (uint16_t compare_index = 0; compare_index < rule_list->nr_rules; compare_index++) { struct rule_field_parser * compare_rule = &rule_list->rules[compare_index]; /* Skip the rule if it's the same rule */ if (rule_index == compare_index) { continue; } /* Skip the rule if it's not active */ if (compare_rule->rule_state != RULE_STATE_ACTIVE) { continue; } /* Compare the rules */ if (rule->adapter.type != compare_rule->adapter.type) { continue; } if (rule->adapter.id != compare_rule->adapter.id) { continue; } if (rule->proto != compare_rule->proto) { continue; } if (rule->proto_mask != compare_rule->proto_mask) { continue; } if (rule->priority != compare_rule->priority) { continue; } if (rule->ether_type != compare_rule->ether_type) { continue; } if (rule->src_port_start != compare_rule->src_port_start) { continue; } if (rule->src_port_end != compare_rule->src_port_end) { continue; } if (rule->dst_port_start != compare_rule->dst_port_start) { continue; } if (rule->dst_port_end != compare_rule->dst_port_end) { continue; } if (rule->src_addr_mask_len != compare_rule->src_addr_mask_len) { continue; } if (rule->dst_addr_mask_len != compare_rule->dst_addr_mask_len) { continue; } if (rule->ether_type == RTE_ETHER_TYPE_IPV4) { if ((rule->src_addr_v4 != compare_rule->src_addr_v4) || (rule->dst_addr_v4 != compare_rule->dst_addr_v4)) { continue; } } else if (rule->ether_type == RTE_ETHER_TYPE_IPV6) { if ((memcmp(rule->src_addr_v6, compare_rule->src_addr_v6, sizeof(rule->src_addr_v6)) != 0) || (memcmp(rule->dst_addr_v6, compare_rule->dst_addr_v6, sizeof(rule->dst_addr_v6)) != 0)) { continue; } } /* If the rules are identical, mark the current rule as inactive */ rule->rule_state = RULE_STATE_INACTIVE; nr_duplicate_rules++; /* Dump the duplicate rule information */ MR_INFO("\nPkt classifier, rule id: %u, is duplicate with rule id: %u.", rule->rule_id, compare_rule->rule_id); MR_INFO("Packet classifier: rule id %u is marked as inactive and will not be loaded.", rule->rule_id); classifier_rule_dump(rule); classifier_rule_dump(compare_rule); break; } } /* If there are no duplicate rules, return */ if (nr_duplicate_rules == 0) { return nr_duplicate_rules; } /* Clean up rule_list by removing invalid rule configurations */ uint16_t new_nr_rules = 0; uint16_t new_nr_static_rules = 0; uint16_t new_nr_dynamic_rules = 0; struct rule_field_parser new_rule_list[MAX_RULES] = {}; for (uint32_t rule_index = 0; rule_index < rule_list->nr_rules; rule_index++) { struct rule_field_parser * rule = &rule_list->rules[rule_index]; if (rule->rule_state == RULE_STATE_ACTIVE) { struct rule_field_parser * new_rule = &new_rule_list[new_nr_rules]; memcpy(new_rule, rule, sizeof(struct rule_field_parser)); if (rule->rule_attr == RULE_ATTR_STATIC) { new_nr_static_rules++; } else if (rule->rule_attr == RULE_ATTR_DYNAMIC) { new_nr_dynamic_rules++; } new_nr_rules++; } } /* Update the rule list */ memset(rule_list->rules, 0, sizeof(rule_list->rules)); memcpy(rule_list->rules, new_rule_list, sizeof(new_rule_list)); rule_list->nr_rules = new_nr_rules; rule_list->nr_static_rules = new_nr_static_rules; rule_list->nr_dynamic_rules = new_nr_dynamic_rules; return nr_duplicate_rules; } int pkt_classifier_rule_parser(const struct sc_main * sc, enum ruleset_type ruleset_type, enum rule_attribute rule_attr, struct rule_list_parsed * out_rule_list) { const char * file_path = NULL; uint16_t * nr_rules_by_type = NULL; switch (rule_attr) { case RULE_ATTR_STATIC: file_path = sc->local_cfgfile; nr_rules_by_type = &out_rule_list->nr_static_rules; break; case RULE_ATTR_DYNAMIC: file_path = sc->local_dyfile; nr_rules_by_type = &out_rule_list->nr_dynamic_rules; break; default: MR_ERROR("Pkt classifier rule parser, cfg_file_type '%u' is invalid.", rule_attr); return RT_ERR; } for (int rule_index = 0; rule_index < MAX_RULES; rule_index++) { /* Retrieve the rule ID */ uint32_t rule_id; char str_section[MR_SYMBOL_MAX] = {}; snprintf(str_section, sizeof(str_section), "classifier_rule:%d", rule_index); int ret = MESA_load_profile_uint_nodef(file_path, str_section, "rule_id", &rule_id); if (ret < 0) { continue; } /* Retrieve the ruleset type; if there's no configured ruleset, break the current rule */ uint32_t load_ruleset_type; MESA_load_profile_uint_def(file_path, str_section, "ruleset_type", &load_ruleset_type, RULESET_TYPE_CLASSIFIER); if (load_ruleset_type != ruleset_type) { continue; } /* Initialize rule field with default values */ struct rule_field_parser rule_field_parser = {}; rule_field_parser.rule_state = RULE_STATE_PENDING; rule_field_parser.src_port_start = 0; rule_field_parser.src_port_end = 65535; rule_field_parser.dst_port_start = 0; rule_field_parser.dst_port_end = 65535; rule_field_parser.ether_type = UINT16_MAX; rule_field_parser.rule_id = rule_id; rule_field_parser.rule_attr = rule_attr; /* Retrieve the proto */ uint32_t proto; MESA_load_profile_uint_def(file_path, str_section, "proto", &proto, UINT32_MAX); if (proto == UINT32_MAX) { /* UINT32_MAX indicates 'any' */ rule_field_parser.proto = 0; rule_field_parser.proto_mask = 0; } else if ((proto == IPPROTO_TCP) || (proto == IPPROTO_UDP)) { rule_field_parser.proto = (uint8_t)proto; rule_field_parser.proto_mask = UINT8_MAX; } else { MR_ERROR("Cfg path : '%s', rule name: '%s', proto must be either 'tcp(6)','udp(17)' or 'any(if not " "configured)'.", file_path, str_section); return RT_ERR; } /* Retrieve source port start and end */ uint32_t src_port_start = 0, src_port_end = 0; int ret_start = MESA_load_profile_uint_def(file_path, str_section, "src_port_start", &src_port_start, 0); int ret_end = MESA_load_profile_uint_def(file_path, str_section, "src_port_end", &src_port_end, 65535); if ((ret_start >= 0) && (ret_end < 0)) { src_port_end = src_port_start; } else if ((ret_start < 0) && (ret_end >= 0)) { src_port_start = src_port_end; } if (src_port_start > src_port_end) { MR_ERROR("Cfg path : '%s', rule name: '%s', src_port_start must be less than or equal to src_port_end.", file_path, str_section); return RT_ERR; } rule_field_parser.src_port_start = (uint16_t)src_port_start; rule_field_parser.src_port_end = (uint16_t)src_port_end; /* Retrieve destination port start and end */ uint32_t dst_port_start = 0, dst_port_end = 0; ret_start = MESA_load_profile_uint_def(file_path, str_section, "dst_port_start", &dst_port_start, 0); ret_end = MESA_load_profile_uint_def(file_path, str_section, "dst_port_end", &dst_port_end, 65535); if ((ret_start >= 0) && (ret_end < 0)) { dst_port_end = dst_port_start; } else if ((ret_start < 0) && (ret_end >= 0)) { dst_port_start = dst_port_end; } if (dst_port_start > dst_port_end) { MR_ERROR("Cfg path : '%s', rule name: '%s', dst_port_start must be less than or equal to dst_port_end.", file_path, str_section); return RT_ERR; } rule_field_parser.dst_port_start = (uint16_t)dst_port_start; rule_field_parser.dst_port_end = (uint16_t)dst_port_end; /* Retrieve the priority */ uint32_t priority; ret = MESA_load_profile_uint_nodef(file_path, str_section, "priority", &priority); if (ret < 0) { MR_ERROR("Cfg path : '%s', rule name: '%s', must config the 'priority'.", file_path, str_section); return RT_ERR; } if (priority >= 16) { MR_ERROR("Cfg path : '%s', rule name: '%s', priority must be 0~15.", file_path, str_section); return RT_ERR; } rule_field_parser.priority = (uint8_t)priority; /* Retrieve the IPv4 source address and mask*/ char str_src_ip_addr_v4[MR_SYMBOL_MAX] = {}; if (MESA_load_profile_string_nodef(file_path, str_section, "src_ip_addr_v4", str_src_ip_addr_v4, sizeof(str_src_ip_addr_v4)) > 0) { rule_field_parser.ether_type = RTE_ETHER_TYPE_IPV4; ret = inet_pton(AF_INET, str_src_ip_addr_v4, &rule_field_parser.src_addr_v4); if ((!ret) && (strcmp(str_src_ip_addr_v4, "0.0.0.0") != 0)) { MR_ERROR("Cfg path : '%s', rule name: '%s', src ip addr is invalid: %s", file_path, str_section, str_src_ip_addr_v4); return RT_ERR; } MESA_load_profile_uint_def(file_path, str_section, "src_ip_mask_v4", &rule_field_parser.src_addr_mask_len, 32); } /* Retrieve the IPv4 destination address and mask */ char str_dst_ip_addr_v4[MR_SYMBOL_MAX] = {}; if (MESA_load_profile_string_nodef(file_path, str_section, "dst_ip_addr_v4", str_dst_ip_addr_v4, sizeof(str_dst_ip_addr_v4)) > 0) { rule_field_parser.ether_type = RTE_ETHER_TYPE_IPV4; ret = inet_pton(AF_INET, str_dst_ip_addr_v4, &rule_field_parser.dst_addr_v4); if ((!ret) && (strcmp(str_dst_ip_addr_v4, "0.0.0.0") != 0)) { MR_ERROR("Cfg path : '%s', rule name: '%s', dst ip addr is invalid: %s", file_path, str_section, str_dst_ip_addr_v4); return RT_ERR; } MESA_load_profile_uint_def(file_path, str_section, "dst_ip_mask_v4", &rule_field_parser.dst_addr_mask_len, 32); } /* Retrieve the IPv6 source address and mask */ char str_src_ip_addr_v6[MR_SYMBOL_MAX] = {}; if (MESA_load_profile_string_nodef(file_path, str_section, "src_ip_addr_v6", str_src_ip_addr_v6, sizeof(str_src_ip_addr_v6)) > 0) { /* Verify the IP version */ if (rule_field_parser.ether_type == RTE_ETHER_TYPE_IPV4) { MR_ERROR("Cfg path : '%s', rule name: '%s', IPv4 or IPv6 only one can be configured.", file_path, str_section); return RT_ERR; } rule_field_parser.ether_type = RTE_ETHER_TYPE_IPV6; ret = inet_pton(AF_INET6, str_src_ip_addr_v6, &rule_field_parser.src_addr_v6); if ((!ret) && (strcmp(str_src_ip_addr_v6, "::") != 0)) { MR_ERROR("Cfg path : '%s', rule name: '%s', src ip addr is invalid: %s", file_path, str_section, str_src_ip_addr_v6); return RT_ERR; } MESA_load_profile_uint_def(file_path, str_section, "src_ip_mask_v6", &rule_field_parser.src_addr_mask_len, 128); } /* Retrieve the IPv6 destination address and mask */ char str_dst_ip_addr_v6[MR_SYMBOL_MAX] = {}; if (MESA_load_profile_string_nodef(file_path, str_section, "dst_ip_addr_v6", str_dst_ip_addr_v6, sizeof(str_dst_ip_addr_v6)) > 0) { /* Verify the IP version */ if (rule_field_parser.ether_type == RTE_ETHER_TYPE_IPV4) { MR_ERROR("Cfg path : '%s', rule name: '%s', IPv4 or IPv6 only one can be configured.", file_path, str_section); return RT_ERR; } rule_field_parser.ether_type = RTE_ETHER_TYPE_IPV6; ret = inet_pton(AF_INET6, str_dst_ip_addr_v6, &rule_field_parser.dst_addr_v6); if ((!ret) && (strcmp(str_dst_ip_addr_v6, "::") != 0)) { MR_ERROR("Cfg path : '%s', rule name: '%s', dst ip addr is invalid: %s", file_path, str_section, str_dst_ip_addr_v6); return RT_ERR; } MESA_load_profile_uint_def(file_path, str_section, "dst_ip_mask_v6", &rule_field_parser.dst_addr_mask_len, 128); } /* Retrieve the action */ uint32_t action; ret = MESA_load_profile_uint_nodef(file_path, str_section, "action", &action); if (ret < 0) { MR_ERROR("Cfg path : '%s', rule name: '%s', must config the 'action'.", file_path, str_section); return RT_ERR; } /* Verify if the action type is invalid */ if (action != ACTION_NF_STEERING) { MR_ERROR("Cfg path : '%s', rule name: '%s', only 'nf_steering(2)' is supported as the action.", file_path, str_section); return RT_ERR; } rule_field_parser.action.type = action; rule_field_parser.action.rule_id = rule_id; /* Retrieve the Network Function Steering (NF steering) configuration */ if (rule_field_parser.action.type == ACTION_NF_STEERING) { /* Retrieve the SID */ uint32_t sid = 0; ret = MESA_load_profile_uint_nodef(file_path, str_section, "sid", &sid); if (ret < 0) { MR_ERROR("Cfg path : '%s', rule name: '%s', must config the 'sid'.", file_path, str_section); return RT_ERR; } rule_field_parser.action.sid = (uint16_t)sid; /* Retrieve the adapter type and ID */ uint32_t ef_adapter_id = UINT32_MAX; uint32_t vwire_id = UINT32_MAX; uint32_t tera_adapter_id = UINT32_MAX; int ef_ret = MESA_load_profile_uint_nodef(file_path, str_section, "ef_adapter_id", &ef_adapter_id); int vwire_ret = MESA_load_profile_uint_nodef(file_path, str_section, "vwire_id", &vwire_id); int tera_ret = MESA_load_profile_uint_nodef(file_path, str_section, "tera_adapter_id", &tera_adapter_id); if (ef_ret >= 0) { rule_field_parser.adapter.type = ADAPTER_TYPE_EF; rule_field_parser.adapter.id = ef_adapter_id; } else if (vwire_ret >= 0) { rule_field_parser.adapter.type = ADAPTER_TYPE_VWIRE; rule_field_parser.adapter.id = vwire_id; } else if (tera_ret >= 0) { rule_field_parser.adapter.type = ADAPTER_TYPE_TERA; rule_field_parser.adapter.id = tera_adapter_id; } else { rule_field_parser.adapter.type = ADAPTER_TYPE_ALL; rule_field_parser.adapter.id = UINT16_MAX; } } /* Copy the rule field information to the output rule list */ struct rule_field_parser * rule_field_item = &out_rule_list->rules[out_rule_list->nr_rules]; memcpy(rule_field_item, &rule_field_parser, sizeof(struct rule_field_parser)); out_rule_list->nr_rules++; (*nr_rules_by_type)++; if (out_rule_list->nr_rules >= MAX_RULES) { MR_ERROR("Cfg path : '%s', rule name: '%s', rules is full.", file_path, str_section); return RT_ERR; } } /* Check for duplicate rules */ uint16_t nr_duplicate_rules = pkt_classifier_duplicate_rules_process(out_rule_list); if (nr_duplicate_rules > 0) { MR_ERROR("Pkt classifier, duplicate rules found: %u", nr_duplicate_rules); } return RT_SUCCESS; }