#include #include #include #include #include #include #include #define FWDSTEP 4 extern int sid_check(uint32_t sid); enum { CLASSIFIER_NEXT_FORWARDER = 0, CLASSIFIER_NEXT_PKT_DROP, CLASSIFIER_NEXT_MAX }; /* Classifier exception reason */ enum cls_excpt_reason { CLS_EXCPT_REASON_PREPEND_SID_ERR = 0, CLS_EXCPT_REASON_MAX }; /* Classifier exception reason string */ static const char * cls_excpt_reason_str[CLS_EXCPT_REASON_MAX] = {"prepend_sid_err"}; struct classifier_stats { volatile uint64_t total_pkts; volatile uint64_t pkts_per_batch; volatile uint64_t hits; volatile uint64_t miss; volatile uint64_t ignore_icmp_pkts; volatile uint64_t ignore_icmp6_pkts; volatile uint64_t excpt_reason[CLS_EXCPT_REASON_MAX]; } __rte_cache_aligned; struct node_classifier_main { unsigned int nr_max_ef_adapters; unsigned int nr_max_vwires; unsigned int nr_max_tera_adapters; unsigned int ignore_all_icmp_pkts; unsigned int ignore_all_icmp6_pkts; struct pkt_classifier_engine * pkt_classifier_engine; struct rule_list_parsed rule_list_parsed; }; static struct node_classifier_main g_classifier_main = {}; static struct classifier_stats stats_per_graph[RTE_MAX_LCORE] = {}; static inline struct node_classifier_main * node_classifier_main_get(void) { return &g_classifier_main; } static inline int classifier_rules_changed(struct rule_list_parsed * old_rules, struct rule_list_parsed * new_rules) { if (old_rules->nr_rules != new_rules->nr_rules) { return RT_SUCCESS; } for (uint32_t new_index = 0; new_index < new_rules->nr_rules; new_index++) { int is_found = 0; struct rule_field_parser * new_rule = &new_rules->rules[new_index]; for (uint32_t old_index = 0; old_index < old_rules->nr_rules; old_index++) { struct rule_field_parser * old_rule = &old_rules->rules[old_index]; if ((new_rule->rule_id == old_rule->rule_id) && (new_rule->priority == old_rule->priority)) { if (memcmp(new_rule, old_rule, sizeof(struct rule_field_parser)) != 0) { return RT_SUCCESS; } is_found = 1; break; } } if (!is_found) { return RT_SUCCESS; } } return RT_ERR; } /* Each ruleset type should implement its own 'rules verify' function to validate its unique match fields. */ static inline int classifier_rules_verify(const struct rule_list_parsed rule_list[]) { for (uint32_t index = 0; index < rule_list->nr_rules; index++) { /* Verify the adapter ID. */ const struct rule_field_parser * rule_field_parser = &rule_list->rules[index]; if (rule_field_parser->adapter.type == ADAPTER_TYPE_EF) { if (ef_adapter_id_check(rule_field_parser->adapter.id) == RT_ERR) { MR_ERROR("The rule ID '%u' with configured ef adapter ID '%u' is invalid.", rule_field_parser->rule_id, rule_field_parser->adapter.id); return RT_ERR; } } else if (rule_field_parser->adapter.type == ADAPTER_TYPE_VWIRE) { if (vwire_id_check(rule_field_parser->adapter.id) == RT_ERR) { MR_ERROR("The rule ID '%u' with configured vwire ID '%u' is invalid.", rule_field_parser->rule_id, rule_field_parser->adapter.id); return RT_ERR; } } else if (rule_field_parser->adapter.type == ADAPTER_TYPE_TERA) { if (tera_adapter_id_check(rule_field_parser->adapter.id) == RT_ERR) { MR_ERROR("The rule ID '%u' with configured tera adapter ID '%u' is invalid.", rule_field_parser->rule_id, rule_field_parser->adapter.id); return RT_ERR; } } /* Only when action.type == ACTION_NF_STEERING, verify the SID. */ if (rule_field_parser->action.type == ACTION_NF_STEERING) { /* Verify the sid. */ if (sid_check(rule_field_parser->action.sid) == RT_ERR) { MR_ERROR("The rule ID '%u' with configured action SID '%u' is invalid.", rule_field_parser->rule_id, rule_field_parser->action.sid); return RT_ERR; } } } return RT_SUCCESS; } static inline uint16_t domain_field_generator_for_classifier(enum adapter_type type, uint16_t adapter_id) { uint16_t domain_field = UINT16_MAX; struct node_classifier_main * classifier_main = node_classifier_main_get(); switch (type) { case ADAPTER_TYPE_EF: domain_field = adapter_id; break; case ADAPTER_TYPE_VWIRE: domain_field = classifier_main->nr_max_ef_adapters + adapter_id; break; case ADAPTER_TYPE_TERA: domain_field = classifier_main->nr_max_ef_adapters + classifier_main->nr_max_vwires + adapter_id; break; default: domain_field = UINT16_MAX; break; } return domain_field; } static inline void rule_list_engine_generator(struct rule_list_parsed * rule_list_parsed, struct rule_list_engine * rule_list_engine) { rule_list_engine->nr_rules = rule_list_parsed->nr_rules; for (uint32_t index = 0; index < rule_list_parsed->nr_rules; index++) { struct rule_field_parser * rule_field_parser = &rule_list_parsed->rules[index]; struct rule_field_engine * rule_field_engine = &rule_list_engine->rules[index]; rule_field_engine->proto = rule_field_parser->proto; rule_field_engine->proto_mask = rule_field_parser->proto_mask; rule_field_engine->priority = rule_field_parser->priority; rule_field_engine->ether_type = rule_field_parser->ether_type; rule_field_engine->src_port_start = rule_field_parser->src_port_start; rule_field_engine->src_port_end = rule_field_parser->src_port_end; rule_field_engine->dst_port_start = rule_field_parser->dst_port_start; rule_field_engine->dst_port_end = rule_field_parser->dst_port_end; rule_field_engine->src_addr_mask_len = rule_field_parser->src_addr_mask_len; rule_field_engine->dst_addr_mask_len = rule_field_parser->dst_addr_mask_len; rule_field_engine->rule_id = rule_field_parser->rule_id; rule_field_engine->domain = domain_field_generator_for_classifier(rule_field_parser->adapter.type, rule_field_parser->adapter.id); switch (rule_field_parser->ether_type) { case RTE_ETHER_TYPE_IPV4: rule_field_engine->src_addr_v4 = rule_field_parser->src_addr_v4; rule_field_engine->dst_addr_v4 = rule_field_parser->dst_addr_v4; break; case RTE_ETHER_TYPE_IPV6: memcpy(rule_field_engine->src_addr_v6, rule_field_parser->src_addr_v6, sizeof(rule_field_engine->src_addr_v6)); memcpy(rule_field_engine->dst_addr_v6, rule_field_parser->dst_addr_v6, sizeof(rule_field_engine->dst_addr_v6)); break; default: memset(rule_field_engine->src_addr_v6, 0, sizeof(rule_field_engine->src_addr_v6)); memset(rule_field_engine->dst_addr_v6, 0, sizeof(rule_field_engine->dst_addr_v6)); break; } memcpy(&rule_field_engine->action, &rule_field_parser->action, sizeof(rule_field_engine->action)); } } int classifier_rule_update(struct sc_main * sc) { /* Parse rules. */ struct node_classifier_main * classifier_main = node_classifier_main_get(); struct pkt_classifier_engine * pkt_classifier_engine = classifier_main->pkt_classifier_engine; struct rule_list_parsed rule_list_parsed = {}; /* Parse static rules */ int ret = pkt_classifier_rule_parser(sc, pkt_classifier_engine_get_ruleset_type(pkt_classifier_engine), RULE_ATTR_STATIC, &rule_list_parsed); if (ret != RT_SUCCESS) { MR_ERROR("Parsing of static packet classifier rule failed."); return RT_ERR; } /* Parse dynamic rules */ ret = pkt_classifier_rule_parser(sc, pkt_classifier_engine_get_ruleset_type(pkt_classifier_engine), RULE_ATTR_DYNAMIC, &rule_list_parsed); if (ret != RT_SUCCESS) { MR_ERROR("Parsing of dynamic packet classifier rule failed."); return RT_ERR; } ret = classifier_rules_changed(&classifier_main->rule_list_parsed, &rule_list_parsed); if (ret != RT_SUCCESS) { MR_INFO("The classifier rules are not changed.\n"); return RT_SUCCESS; } /* Verify the classifier rule. */ ret = classifier_rules_verify(&rule_list_parsed); if (ret != RT_SUCCESS) { MR_ERROR("Check classifier rules failed."); return RT_ERR; } /* Generate the list of engine rules. */ struct rule_list_engine rule_list_engine = {}; rule_list_engine_generator(&rule_list_parsed, &rule_list_engine); /* Construct the packet classifier engine. */ ret = pkt_classifier_engine_build(pkt_classifier_engine, &rule_list_engine); if (ret != RT_SUCCESS) { MR_ERROR("Pkt classifier engine build failed"); return RT_ERR; } /* Display information about the packet classifier rules. */ memcpy(&classifier_main->rule_list_parsed, &rule_list_parsed, sizeof(struct rule_list_parsed)); classifier_rule_list_dump(&classifier_main->rule_list_parsed); pkt_classifier_engine_info_dump(pkt_classifier_engine); return RT_SUCCESS; } int classifier_init(struct sc_main * sc) { /* Initialize the main instance of the classifier. */ struct node_classifier_main * classifier_main = node_classifier_main_get(); memset(classifier_main, 0, sizeof(struct node_classifier_main)); /* Load to ignore icmp and icmpv6 options */ MESA_load_profile_uint_def(sc->local_cfgfile, "classifiers", "ignore_all_icmp_pkts", &classifier_main->ignore_all_icmp_pkts, 0); MESA_load_profile_uint_def(sc->local_cfgfile, "classifiers", "ignore_all_icmp6_pkts", &classifier_main->ignore_all_icmp6_pkts, 0); /* Create a new packet classifier engine. */ classifier_main->nr_max_ef_adapters = nr_max_ef_adapters_get(); classifier_main->nr_max_vwires = nr_max_vwires_get(); classifier_main->nr_max_tera_adapters = nr_max_tera_adapters_get(); uint16_t nr_domains = classifier_main->nr_max_ef_adapters; nr_domains += classifier_main->nr_max_vwires; nr_domains += +classifier_main->nr_max_tera_adapters; /* Create pkt classifier engine */ struct pkt_classifier_engine * pkt_classifier_engine = pkt_classifier_engine_create( sc->nr_io_thread, &sc->cpu_set_io, nr_domains, RULESET_TYPE_CLASSIFIER); if (pkt_classifier_engine == NULL) { MR_ERROR("Failed to create the packet classifier engine for the classifier node."); return RT_ERR; } else { classifier_main->pkt_classifier_engine = pkt_classifier_engine; } /* Parse static rules */ struct rule_list_parsed rule_list_parsed = {}; int ret = pkt_classifier_rule_parser(sc, pkt_classifier_engine_get_ruleset_type(pkt_classifier_engine), RULE_ATTR_STATIC, &rule_list_parsed); if (ret != RT_SUCCESS) { MR_ERROR("Parsing of static packet classifier rule failed."); return RT_ERR; } /* Parse dynamic rules */ ret = pkt_classifier_rule_parser(sc, pkt_classifier_engine_get_ruleset_type(pkt_classifier_engine), RULE_ATTR_DYNAMIC, &rule_list_parsed); if (ret != RT_SUCCESS) { MR_ERROR("Parsing of dynamic packet classifier rule failed."); return RT_ERR; } /* Verify the classifier rule. */ ret = classifier_rules_verify(&rule_list_parsed); if (ret != RT_SUCCESS) { MR_ERROR("Check classifier rules failed."); return RT_ERR; } /* Generate the list of engine rules. */ struct rule_list_engine rule_list_engine = {}; rule_list_engine_generator(&rule_list_parsed, &rule_list_engine); /* Construct the packet classifier engine. */ ret = pkt_classifier_engine_build(pkt_classifier_engine, &rule_list_engine); if (ret != RT_SUCCESS) { MR_ERROR("Pkt classifier engine build failed"); return RT_ERR; } /* Display information about the packet classifier rules. */ memcpy(&classifier_main->rule_list_parsed, &rule_list_parsed, sizeof(struct rule_list_parsed)); classifier_rule_list_dump(&classifier_main->rule_list_parsed); pkt_classifier_engine_info_dump(pkt_classifier_engine); return RT_SUCCESS; } /* Generate and store the trace information */ static __rte_always_inline void gen_store_trace_info(struct rte_node * node, struct rte_mbuf * mbuf, uint16_t next_node_index, struct classifier_stats * stats, struct match_result_engine * result, uint8_t ignore_icmp, enum cls_excpt_reason excpt_reason, uint8_t measurement_type) { /* Populate the next node infomation */ char str_record[MR_STRING_MAX]; int len = snprintf(str_record, sizeof(str_record), "recv packet. next node:%s", node->nodes[next_node_index]->name); if (unlikely(excpt_reason != CLS_EXCPT_REASON_MAX)) { assert(excpt_reason < CLS_EXCPT_REASON_MAX); len += snprintf(str_record + len, sizeof(str_record) - len, ", excpt:%s", cls_excpt_reason_str[excpt_reason]); } /* Populate the classifier result */ if (unlikely(result->nr_actions == 0)) { if (unlikely(ignore_icmp)) { len += snprintf(str_record + len, sizeof(str_record) - len, ", res:ignore icmp"); } else { len += snprintf(str_record + len, sizeof(str_record) - len, ", res:miss"); } } else { len += snprintf(str_record + len, sizeof(str_record) - len, ", res:hit, pt:%u", result->pass_through); for (uint8_t i = 0; i < result->nr_actions; i++) { struct action * action = &result->actions[i]; len += snprintf(str_record + len, sizeof(str_record) - len, ", [id:%u, prio:%u, prep sid:%u]", action->rule_id, i, action->sid); } } /* Emit the trace record */ struct dp_trace_record_meta meta = { .measurement_type = measurement_type, .appsym = MR_TRACE_APPSYM, .module = node->name}; dp_trace_record_emit_str(sc_main_get()->trace, mbuf, rte_lcore_id(), &meta, str_record); } /* ++++++++++++++++++++++++++++++++++++++++++++ Classifier Node Process ++++++++++++++++++++++++++++++++++++++++++++ */ static __rte_always_inline uint16_t classifier_node_process(struct rte_graph * graph, struct rte_node * node, void ** objs, uint16_t cnt) { struct node_classifier_main * classifier_main = node_classifier_main_get(); struct pkt_classifier_engine * pkt_classifier_engine = classifier_main->pkt_classifier_engine; unsigned int ignore_all_icmp_pkts = classifier_main->ignore_all_icmp_pkts; unsigned int ignore_all_icmp6_pkts = classifier_main->ignore_all_icmp6_pkts; unsigned int n_left_from = cnt; uint32_t lcore_id = rte_lcore_id(); struct sc_main * sc = sc_main_get(); struct classifier_stats stats = {}; struct rte_mbuf ** mbufs = (struct rte_mbuf **)objs; while (n_left_from >= FWDSTEP) { enum cls_excpt_reason excpt_reason = CLS_EXCPT_REASON_MAX; struct rte_mbuf ** pending_mbufs = mbufs; mbufs += FWDSTEP; n_left_from -= FWDSTEP; struct mrb_metadata * mrb_metadata[FWDSTEP]; for (uint8_t pkt_idx = 0; pkt_idx < FWDSTEP; pkt_idx++) { mrb_metadata[pkt_idx] = mrbuf_cz_data(pending_mbufs[pkt_idx], MR_NODE_CTRLZONE_ID); dp_trace_filter_exec(sc->trace, pending_mbufs[pkt_idx], 0, lcore_id); } uint16_t domain_field_for_pkts[FWDSTEP]; for (uint8_t pkt_idx = 0; pkt_idx < FWDSTEP; pkt_idx++) { struct mrb_metadata * metadata = mrb_metadata[pkt_idx]; domain_field_for_pkts[pkt_idx] = domain_field_generator_for_classifier(metadata->adapter_type, metadata->adapter_id); } struct match_result_engine results[FWDSTEP]; pkt_classifier_engine_multi_match(pkt_classifier_engine, lcore_id, pending_mbufs, FWDSTEP, domain_field_for_pkts, results); /* All the ICMP packet, go direct to egress, not for nfs */ uint8_t ignore_icmp_pkts[FWDSTEP] = {}; if (ignore_all_icmp_pkts) { for (uint8_t pkt_idx = 0; pkt_idx < FWDSTEP; pkt_idx++) { struct pkt_parser_result * pkt_parser_result = &mrb_metadata[pkt_idx]->pkt_parser_result; void * icmp_start = complex_layer_jump_to_outermost( pkt_parser_result, rte_pktmbuf_mtod(pending_mbufs[pkt_idx], void *), LAYER_TYPE_ID_ICMP); if (unlikely(icmp_start != NULL)) { ignore_icmp_pkts[pkt_idx] = 1; results[pkt_idx].nr_actions = 0; stats.ignore_icmp_pkts++; } } } if (ignore_all_icmp6_pkts) { for (uint8_t pkt_idx = 0; pkt_idx < FWDSTEP; pkt_idx++) { struct pkt_parser_result * pkt_parser_result = &mrb_metadata[pkt_idx]->pkt_parser_result; void * icmp_start = complex_layer_jump_to_outermost( pkt_parser_result, rte_pktmbuf_mtod(pending_mbufs[pkt_idx], void *), LAYER_TYPE_ID_ICMP6); if (unlikely(icmp_start != NULL)) { ignore_icmp_pkts[pkt_idx] = 1; results[pkt_idx].nr_actions = 0; stats.ignore_icmp6_pkts++; } } } /* Insert sid. */ for (uint8_t pkt_idx = 0; pkt_idx < FWDSTEP; pkt_idx++) { uint16_t prepend_sids[16]; uint8_t nr_actions = results[pkt_idx].nr_actions; struct action * actions = results[pkt_idx].actions; if (likely(nr_actions != 0)) { if (mrb_metadata[pkt_idx]->dir == 0) { for (uint8_t action_idx = 0; action_idx < nr_actions; action_idx++) { prepend_sids[action_idx] = actions[action_idx].sid; } } else { for (uint8_t action_idx = 0; action_idx < nr_actions; action_idx++) { prepend_sids[action_idx] = actions[nr_actions - action_idx - 1].sid; } } if (unlikely(sid_list_prepend(&mrb_metadata[pkt_idx]->sid_list, prepend_sids, nr_actions) != 0)) { excpt_reason = CLS_EXCPT_REASON_PREPEND_SID_ERR; stats.excpt_reason[excpt_reason]++; } stats.hits++; } else { if (likely(!ignore_icmp_pkts[pkt_idx])) stats.miss++; } if (unlikely(dp_trace_record_can_emit(pending_mbufs[pkt_idx], DP_TRACE_MEASUREMENT_TYPE_TELEMETRY))) { struct mr_dev_desc * dev_desc = NULL; dev_desc = mr_dev_desc_lookup_by_port_id(sc->devmgr_main, mrb_metadata[pkt_idx]->port_ingress); gen_store_telemetry_info_rx(node, pending_mbufs[pkt_idx], dev_desc, graph->id); } /* Check if tracing is enabled for the current Mbuf */ if (unlikely(dp_trace_record_can_emit(pending_mbufs[pkt_idx], DP_TRACE_MEASUREMENT_TYPE_TRACE))) { gen_store_trace_info(node, pending_mbufs[pkt_idx], CLASSIFIER_NEXT_FORWARDER, &stats, &results[pkt_idx], ignore_icmp_pkts[pkt_idx], excpt_reason, DP_TRACE_MEASUREMENT_TYPE_TRACE); gen_store_trace_info_sid_list(node, pending_mbufs[pkt_idx]); } } } while (n_left_from > 0) { enum cls_excpt_reason excpt_reason = CLS_EXCPT_REASON_MAX; struct rte_mbuf * mbuf0 = mbufs[0]; mbufs += 1; n_left_from -= 1; dp_trace_filter_exec(sc_main_get()->trace, mbuf0, 0, lcore_id); struct match_result_engine result; struct mrb_metadata * mrb_metadata = mrbuf_cz_data(mbuf0, MR_NODE_CTRLZONE_ID); uint16_t pattern_group_id = domain_field_generator_for_classifier(mrb_metadata->adapter_type, mrb_metadata->adapter_id); pkt_classifier_engine_multi_match(pkt_classifier_engine, lcore_id, &mbuf0, 1, &pattern_group_id, &result); /* All the ICMP packet, go direct to egress, not for nfs */ uint8_t ignore_icmp_pkt = 0; if (ignore_all_icmp_pkts) { struct pkt_parser_result * pkt_parser_result = &mrb_metadata->pkt_parser_result; void * icmp_start = complex_layer_jump_to_outermost(pkt_parser_result, rte_pktmbuf_mtod(mbuf0, void *), LAYER_TYPE_ID_ICMP); if (unlikely(icmp_start != NULL)) { ignore_icmp_pkt = 1; result.nr_actions = 0; stats.ignore_icmp_pkts++; } } if (ignore_all_icmp6_pkts) { struct pkt_parser_result * pkt_parser_result = &mrb_metadata->pkt_parser_result; void * icmp_start = complex_layer_jump_to_outermost(pkt_parser_result, rte_pktmbuf_mtod(mbuf0, void *), LAYER_TYPE_ID_ICMP6); if (unlikely(icmp_start != NULL)) { ignore_icmp_pkt = 1; result.nr_actions = 0; stats.ignore_icmp6_pkts++; } } /* Insert sid. */ uint16_t prepend_sids[16]; uint8_t nr_actions = result.nr_actions; if (likely(nr_actions != 0)) { if (mrb_metadata->dir == 0) { for (uint8_t action_idx = 0; action_idx < nr_actions; action_idx++) { prepend_sids[action_idx] = result.actions[action_idx].sid; } } else { for (uint8_t action_idx = 0; action_idx < nr_actions; action_idx++) { prepend_sids[action_idx] = result.actions[nr_actions - action_idx - 1].sid; } } if (unlikely(sid_list_prepend(&mrb_metadata->sid_list, prepend_sids, nr_actions) != 0)) { excpt_reason = CLS_EXCPT_REASON_PREPEND_SID_ERR; stats.excpt_reason[excpt_reason]++; } stats.hits++; } else { if (likely(!ignore_icmp_pkt)) stats.miss++; } if (unlikely(dp_trace_record_can_emit(mbuf0, DP_TRACE_MEASUREMENT_TYPE_TELEMETRY))) { struct mr_dev_desc * dev_desc = NULL; dev_desc = mr_dev_desc_lookup_by_port_id(sc->devmgr_main, mrb_metadata->port_ingress); gen_store_telemetry_info_rx(node, mbuf0, dev_desc, graph->id); } /* Check if tracing is enabled for the current Mbuf */ if (unlikely(dp_trace_record_can_emit(mbuf0, DP_TRACE_MEASUREMENT_TYPE_TRACE))) { gen_store_trace_info(node, mbuf0, CLASSIFIER_NEXT_FORWARDER, &stats, &result, ignore_icmp_pkt, excpt_reason, DP_TRACE_MEASUREMENT_TYPE_TRACE); gen_store_trace_info_sid_list(node, mbuf0); } } /* Forward to the next node. */ rte_node_next_stream_move(graph, node, CLASSIFIER_NEXT_FORWARDER); /* Update the graph statistics */ struct classifier_stats * graph_stats = &stats_per_graph[graph->id]; graph_stats->total_pkts += cnt; graph_stats->pkts_per_batch = cnt; graph_stats->hits += stats.hits; graph_stats->miss += stats.miss; graph_stats->ignore_icmp_pkts += stats.ignore_icmp_pkts; graph_stats->ignore_icmp6_pkts += stats.ignore_icmp6_pkts; for (uint8_t i = 0; i < CLS_EXCPT_REASON_MAX; i++) { graph_stats->excpt_reason[i] += stats.excpt_reason[i]; } return cnt; } static struct rte_node_register classifier_node_base = { .process = classifier_node_process, .name = "classifier", .init = NULL, .nb_edges = CLASSIFIER_NEXT_MAX, .next_nodes = { [CLASSIFIER_NEXT_FORWARDER] = "forwarder", [CLASSIFIER_NEXT_PKT_DROP] = "pkt_drop_trap", }, }; RTE_NODE_REGISTER(classifier_node_base); cJSON * classifier_node_monit_loop(struct sc_main * sc) { cJSON * json_root = cJSON_CreateObject(); unsigned int nr_graphs = sc->nr_io_thread; uint64_t total_pkts[nr_graphs]; uint64_t pkts_per_batch[nr_graphs]; uint64_t total_hits[nr_graphs]; uint64_t total_miss[nr_graphs]; uint64_t total_ignore_icmp_pkts[nr_graphs]; uint64_t total_ignore_icmp6_pkts[nr_graphs]; uint64_t total_excpt_reason[CLS_EXCPT_REASON_MAX][nr_graphs]; for (uint32_t graph_id = 0; graph_id < nr_graphs; graph_id++) { struct classifier_stats * stats = &stats_per_graph[graph_id]; if (stats->total_pkts == 0) { total_pkts[graph_id] = 0; pkts_per_batch[graph_id] = 0; total_hits[graph_id] = 0; total_miss[graph_id] = 0; total_ignore_icmp_pkts[graph_id] = 0; total_ignore_icmp6_pkts[graph_id] = 0; memset(total_excpt_reason, 0, sizeof(total_excpt_reason)); continue; } total_pkts[graph_id] = stats->total_pkts; pkts_per_batch[graph_id] = stats->pkts_per_batch; total_hits[graph_id] = stats->hits; total_miss[graph_id] = stats->miss; total_ignore_icmp_pkts[graph_id] = stats->ignore_icmp_pkts; total_ignore_icmp6_pkts[graph_id] = stats->ignore_icmp6_pkts; for (uint8_t i = 0; i < CLS_EXCPT_REASON_MAX; i++) { total_excpt_reason[i][graph_id] = stats->excpt_reason[i]; } } cJSON * json_total_pkts = cJSON_CreateIntArray((const int *)total_pkts, nr_graphs); cJSON_AddItemToObject(json_root, "classifier, total_pkts", json_total_pkts); cJSON * json_pkts_per_batch = cJSON_CreateIntArray((const int *)pkts_per_batch, nr_graphs); cJSON_AddItemToObject(json_root, "classifier, pkts_per_batch", json_pkts_per_batch); cJSON * json_total_hits = cJSON_CreateIntArray((const int *)total_hits, nr_graphs); cJSON_AddItemToObject(json_root, "classifier, total_hits", json_total_hits); cJSON * json_total_miss = cJSON_CreateIntArray((const int *)total_miss, nr_graphs); cJSON_AddItemToObject(json_root, "classifier, total_miss", json_total_miss); cJSON * json_total_ignore_icmp_pkts = cJSON_CreateIntArray((const int *)total_ignore_icmp_pkts, nr_graphs); cJSON_AddItemToObject(json_root, "classifier, total_ignore_icmp_pkts", json_total_ignore_icmp_pkts); cJSON * json_total_ignore_icmp6_pkts = cJSON_CreateIntArray((const int *)total_ignore_icmp6_pkts, nr_graphs); cJSON_AddItemToObject(json_root, "classifier, total_ignore_icmp6_pkts", json_total_ignore_icmp6_pkts); for (uint8_t i = 0; i < CLS_EXCPT_REASON_MAX; i++) { char str_title[MR_STRING_MAX]; snprintf(str_title, sizeof(str_title), "classifier, %s", cls_excpt_reason_str[i]); cJSON * json_total_excpt_reason = cJSON_CreateIntArray((const int *)total_excpt_reason[i], nr_graphs); cJSON_AddItemToObject(json_root, str_title, json_total_excpt_reason); } return json_root; }