#include "ogs-app.h" #include "context.h" #include "metrics.h" typedef struct smf_metrics_spec_def_s { unsigned int type; const char *name; const char *description; int initial_val; unsigned int num_labels; const char **labels; } smf_metrics_spec_def_t; /* Helper generic functions: */ static int smf_metrics_init_inst(ogs_metrics_inst_t **inst, ogs_metrics_spec_t **specs, unsigned int len, unsigned int num_labels, const char **labels) { unsigned int i; for (i = 0; i < len; i++) inst[i] = ogs_metrics_inst_new(specs[i], num_labels, labels); return OGS_OK; } static int smf_metrics_free_inst(ogs_metrics_inst_t **inst, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) ogs_metrics_inst_free(inst[i]); memset(inst, 0, sizeof(inst[0]) * len); return OGS_OK; } static int smf_metrics_init_spec(ogs_metrics_context_t *ctx, ogs_metrics_spec_t **dst, smf_metrics_spec_def_t *src, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) { dst[i] = ogs_metrics_spec_new(ctx, src[i].type, src[i].name, src[i].description, src[i].initial_val, src[i].num_labels, src[i].labels); } return OGS_OK; } /* GLOBAL */ ogs_metrics_spec_t *smf_metrics_spec_global[_SMF_METR_GLOB_MAX]; ogs_metrics_inst_t *smf_metrics_inst_global[_SMF_METR_GLOB_MAX]; smf_metrics_spec_def_t smf_metrics_spec_def_global[_SMF_METR_GLOB_MAX] = { /* Global Counters: */ [SMF_METR_GLOB_CTR_GTP_NEW_NODE_FAILED] = { .type = OGS_METRICS_METRIC_TYPE_COUNTER, .name = "gtp_new_node_failed", .description = "Unable to allocate new GTP (peer) Node", }, [SMF_METR_GLOB_CTR_GN_RX_PARSE_FAILED] = { .type = OGS_METRICS_METRIC_TYPE_COUNTER, .name = "gn_rx_parse_failed", .description = "Received GTPv1C messages discarded due to parsing failure", }, [SMF_METR_GLOB_CTR_GN_RX_CREATEPDPCTXREQ] = { .type = OGS_METRICS_METRIC_TYPE_COUNTER, .name = "gn_rx_createpdpcontextreq", .description = "Received GTPv1C CreatePDPContextRequest messages", }, [SMF_METR_GLOB_CTR_GN_RX_DELETEPDPCTXREQ] = { .type = OGS_METRICS_METRIC_TYPE_COUNTER, .name = "gn_rx_deletepdpcontextreq", .description = "Received GTPv1C DeletePDPContextRequest messages", }, [SMF_METR_GLOB_CTR_S5C_RX_PARSE_FAILED] = { .type = OGS_METRICS_METRIC_TYPE_COUNTER, .name = "s5c_rx_parse_failed", .description = "Received GTPv2C messages discarded due to parsing failure", }, [SMF_METR_GLOB_CTR_S5C_RX_CREATESESSIONREQ] = { .type = OGS_METRICS_METRIC_TYPE_COUNTER, .name = "s5c_rx_createsession", .description = "Received GTPv2C CreateSessionRequest messages", }, [SMF_METR_GLOB_CTR_S5C_RX_DELETESESSIONREQ] = { .type = OGS_METRICS_METRIC_TYPE_COUNTER, .name = "s5c_rx_deletesession", .description = "Received GTPv2C DeleteSessionRequest messages", }, [SMF_METR_GLOB_CTR_SM_N4SESSIONESTABREQ] = { .type = OGS_METRICS_METRIC_TYPE_COUNTER, .name = "fivegs_smffunction_sm_n4sessionestabreq", .description = "Number of requested N4 session establishments evidented by SMF", }, [SMF_METR_GLOB_CTR_SM_N4SESSIONREPORT] = { .type = OGS_METRICS_METRIC_TYPE_COUNTER, .name = "fivegs_smffunction_sm_n4sessionreport", .description = "Number of requested N4 session reports evidented by SMF", }, [SMF_METR_GLOB_CTR_SM_N4SESSIONREPORTSUCC] = { .type = OGS_METRICS_METRIC_TYPE_COUNTER, .name = "fivegs_smffunction_sm_n4sessionreportsucc", .description = "Number of successful N4 session reports evidented by SMF", }, /* Global Gauges: */ [SMF_METR_GLOB_GAUGE_UES_ACTIVE] = { .type = OGS_METRICS_METRIC_TYPE_GAUGE, .name = "ues_active", .description = "Active User Equipments", }, [SMF_METR_GLOB_GAUGE_BEARERS_ACTIVE] = { .type = OGS_METRICS_METRIC_TYPE_GAUGE, .name = "bearers_active", .description = "Active Bearers", }, [SMF_METR_GLOB_GAUGE_GTP1_PDPCTXS_ACTIVE] = { .type = OGS_METRICS_METRIC_TYPE_GAUGE, .name = "gtp1_pdpctxs_active", .description = "Active GTPv1 PDP Contexts (GGSN)", }, [SMF_METR_GLOB_GAUGE_GTP2_SESSIONS_ACTIVE] = { .type = OGS_METRICS_METRIC_TYPE_GAUGE, .name = "gtp2_sessions_active", .description = "Active GTPv2 Sessions (PGW)", }, [SMF_METR_GLOB_GAUGE_GTP_PEERS_ACTIVE] = { .type = OGS_METRICS_METRIC_TYPE_GAUGE, .name = "gtp_peers_active", .description = "Active GTP peers", }, }; int smf_metrics_init_inst_global(void) { return smf_metrics_init_inst(smf_metrics_inst_global, smf_metrics_spec_global, _SMF_METR_GLOB_MAX, 0, NULL); } int smf_metrics_free_inst_global(void) { return smf_metrics_free_inst(smf_metrics_inst_global, _SMF_METR_GLOB_MAX); } /* GTP NODE: */ const char *labels_gtp_node[] = { "addr" }; #define SMF_METR_GTP_NODE_CTR_ENTRY(_id, _name, _desc) \ [_id] = { \ .type = OGS_METRICS_METRIC_TYPE_COUNTER, \ .name = _name, \ .description = _desc, \ .num_labels = OGS_ARRAY_SIZE(labels_gtp_node), \ .labels = labels_gtp_node, \ }, ogs_metrics_spec_t *smf_metrics_spec_gtp_node[_SMF_METR_GTP_NODE_MAX]; smf_metrics_spec_def_t smf_metrics_spec_def_gtp_node[_SMF_METR_GTP_NODE_MAX] = { /* Global Counters: */ SMF_METR_GTP_NODE_CTR_ENTRY( SMF_METR_GTP_NODE_CTR_GN_RX_PARSE_FAILED, "gtp_node_gn_rx_parse_failed", "Received GTPv1C messages discarded due to parsing failure") SMF_METR_GTP_NODE_CTR_ENTRY( SMF_METR_GTP_NODE_CTR_GN_RX_CREATEPDPCTXREQ, "gtp_node_gn_rx_createpdpcontextreq", "Received GTPv1C CreatePDPContextRequest messages") SMF_METR_GTP_NODE_CTR_ENTRY( SMF_METR_GTP_NODE_CTR_GN_RX_DELETEPDPCTXREQ, "gtp_node_gn_rx_deletepdpcontextreq", "Received GTPv1C DeletePDPContextRequest messages") SMF_METR_GTP_NODE_CTR_ENTRY( SMF_METR_GTP_NODE_CTR_S5C_RX_PARSE_FAILED, "gtp_node_s5c_rx_parse_failed", "Received GTPv2C messages discarded due to parsing failure") SMF_METR_GTP_NODE_CTR_ENTRY( SMF_METR_GTP_NODE_CTR_S5C_RX_CREATESESSIONREQ, "gtp_node_s5c_rx_createsession", "Received GTPv2C CreateSessionRequest messages") SMF_METR_GTP_NODE_CTR_ENTRY( SMF_METR_GTP_NODE_CTR_S5C_RX_DELETESESSIONREQ, "gtp_node_s5c_rx_deletesession", "Received GTPv2C DeleteSessionRequest messages") }; int smf_metrics_init_inst_gtp_node(ogs_metrics_inst_t **inst, const char *addr) { return smf_metrics_init_inst(inst, smf_metrics_spec_gtp_node, _SMF_METR_GTP_NODE_MAX, smf_metrics_spec_def_gtp_node->num_labels, (const char *[]){ addr }); } int smf_metrics_free_inst_gtp_node(ogs_metrics_inst_t **inst) { return smf_metrics_free_inst(inst, _SMF_METR_GTP_NODE_MAX); } /* BY SLICE */ const char *labels_slice[] = { "plmnid", "snssai" }; #define SMF_METR_BY_SLICE_GAUGE_ENTRY(_id, _name, _desc) \ [_id] = { \ .type = OGS_METRICS_METRIC_TYPE_GAUGE, \ .name = _name, \ .description = _desc, \ .num_labels = OGS_ARRAY_SIZE(labels_slice), \ .labels = labels_slice, \ }, ogs_metrics_spec_t *smf_metrics_spec_by_slice[_SMF_METR_BY_SLICE_MAX]; ogs_hash_t *metrics_hash_by_slice = NULL; /* hash table for SLICE labels */ smf_metrics_spec_def_t smf_metrics_spec_def_by_slice[_SMF_METR_BY_SLICE_MAX] = { /* Gauges: */ SMF_METR_BY_SLICE_GAUGE_ENTRY( SMF_METR_GAUGE_SM_SESSIONNBR, "fivegs_smffunction_sm_sessionnbr", "Active Sessions") }; void smf_metrics_init_by_slice(void); int smf_metrics_free_inst_by_slice(ogs_metrics_inst_t **inst); typedef struct smf_metric_key_by_slice_s { ogs_plmn_id_t plmn_id; ogs_s_nssai_t snssai; smf_metric_type_by_slice_t t; } smf_metric_key_by_slice_t; void smf_metrics_init_by_slice(void) { metrics_hash_by_slice = ogs_hash_make(); ogs_assert(metrics_hash_by_slice); } void smf_metrics_inst_by_slice_add(ogs_plmn_id_t *plmn, ogs_s_nssai_t *snssai, smf_metric_type_by_slice_t t, int val) { ogs_metrics_inst_t *metrics = NULL; smf_metric_key_by_slice_t *slice_key; slice_key = ogs_calloc(1, sizeof(*slice_key)); ogs_assert(slice_key); if (plmn) { slice_key->plmn_id = *plmn; } if (snssai) { slice_key->snssai = *snssai; } else { slice_key->snssai.sst = 0; slice_key->snssai.sd.v = OGS_S_NSSAI_NO_SD_VALUE; } slice_key->t = t; metrics = ogs_hash_get(metrics_hash_by_slice, slice_key, sizeof(*slice_key)); if (!metrics) { char plmn_id[OGS_PLMNIDSTRLEN] = ""; char *s_nssai = NULL; if (plmn) { ogs_plmn_id_to_string(plmn, plmn_id); } if (snssai) { s_nssai = ogs_sbi_s_nssai_to_string_plain(snssai); } else { s_nssai = ogs_strdup(""); } metrics = ogs_metrics_inst_new(smf_metrics_spec_by_slice[t], smf_metrics_spec_def_by_slice->num_labels, (const char *[]){ plmn_id, s_nssai }); ogs_assert(metrics); ogs_hash_set(metrics_hash_by_slice, slice_key, sizeof(*slice_key), metrics); if (s_nssai) ogs_free(s_nssai); } else { ogs_free(slice_key); } ogs_metrics_inst_add(metrics, val); } int smf_metrics_free_inst_by_slice(ogs_metrics_inst_t **inst) { return smf_metrics_free_inst(inst, _SMF_METR_BY_SLICE_MAX); } /* BY SLICE and 5QI */ const char *labels_5qi[] = { "plmnid", "snssai", "fiveqi" }; #define SMF_METR_BY_5QI_GAUGE_ENTRY(_id, _name, _desc) \ [_id] = { \ .type = OGS_METRICS_METRIC_TYPE_GAUGE, \ .name = _name, \ .description = _desc, \ .num_labels = OGS_ARRAY_SIZE(labels_5qi), \ .labels = labels_5qi, \ }, ogs_metrics_spec_t *smf_metrics_spec_by_5qi[_SMF_METR_BY_5QI_MAX]; ogs_hash_t *metrics_hash_by_5qi = NULL; /* hash table for 5QI label */ smf_metrics_spec_def_t smf_metrics_spec_def_by_5qi[_SMF_METR_BY_5QI_MAX] = { /* Gauges: */ SMF_METR_BY_5QI_GAUGE_ENTRY( SMF_METR_GAUGE_SM_QOSFLOWNBR, "fivegs_smffunction_sm_qos_flow_nbr", "Number of QoS flows at the SMF") }; void smf_metrics_init_by_5qi(void); int smf_metrics_free_inst_by_5qi(ogs_metrics_inst_t **inst); typedef struct smf_metric_key_by_5qi_s { ogs_plmn_id_t plmn_id; ogs_s_nssai_t snssai; uint8_t fiveqi; smf_metric_type_by_5qi_t t; } smf_metric_key_by_5qi_t; void smf_metrics_init_by_5qi(void) { metrics_hash_by_5qi = ogs_hash_make(); ogs_assert(metrics_hash_by_5qi); } void smf_metrics_inst_by_5qi_add(ogs_plmn_id_t *plmn, ogs_s_nssai_t *snssai, uint8_t fiveqi, smf_metric_type_by_5qi_t t, int val) { ogs_metrics_inst_t *metrics = NULL; smf_metric_key_by_5qi_t *fiveqi_key; fiveqi_key = ogs_calloc(1, sizeof(*fiveqi_key)); ogs_assert(fiveqi_key); if (plmn) { fiveqi_key->plmn_id = *plmn; } if (snssai) { fiveqi_key->snssai = *snssai; } else { fiveqi_key->snssai.sst = 0; fiveqi_key->snssai.sd.v = OGS_S_NSSAI_NO_SD_VALUE; } fiveqi_key->fiveqi = fiveqi; fiveqi_key->t = t; metrics = ogs_hash_get(metrics_hash_by_5qi, fiveqi_key, sizeof(*fiveqi_key)); if (!metrics) { char plmn_id[OGS_PLMNIDSTRLEN] = ""; char *s_nssai = NULL; char fiveqi_str[4]; if (plmn) { ogs_plmn_id_to_string(plmn, plmn_id); } if (snssai) { s_nssai = ogs_sbi_s_nssai_to_string_plain(snssai); } else { s_nssai = ogs_strdup(""); } ogs_snprintf(fiveqi_str, sizeof(fiveqi_str), "%d", fiveqi); metrics = ogs_metrics_inst_new(smf_metrics_spec_by_5qi[t], smf_metrics_spec_def_by_5qi->num_labels, (const char *[]){ plmn_id, s_nssai, fiveqi_str }); ogs_assert(metrics); ogs_hash_set(metrics_hash_by_5qi, fiveqi_key, sizeof(*fiveqi_key), metrics); if (s_nssai) ogs_free(s_nssai); } else { ogs_free(fiveqi_key); } ogs_metrics_inst_add(metrics, val); } int smf_metrics_free_inst_by_5qi(ogs_metrics_inst_t **inst) { return smf_metrics_free_inst(inst, _SMF_METR_BY_5QI_MAX); } /* BY CAUSE */ const char *labels_cause[] = { "cause" }; #define SMF_METR_BY_CAUSE_CTR_ENTRY(_id, _name, _desc) \ [_id] = { \ .type = OGS_METRICS_METRIC_TYPE_COUNTER, \ .name = _name, \ .description = _desc, \ .num_labels = OGS_ARRAY_SIZE(labels_cause), \ .labels = labels_cause, \ }, ogs_metrics_spec_t *smf_metrics_spec_by_cause[_SMF_METR_BY_CAUSE_MAX]; ogs_hash_t *metrics_hash_by_cause = NULL; /* hash table for CAUSE labels */ smf_metrics_spec_def_t smf_metrics_spec_def_by_cause[_SMF_METR_BY_CAUSE_MAX] = { /* Counters: */ SMF_METR_BY_CAUSE_CTR_ENTRY( SMF_METR_CTR_SM_N4SESSIONESTABFAIL, "fivegs_smffunction_sm_n4sessionestabfail", "Number of failed N4 session establishments evidented by SMF") }; void smf_metrics_init_by_cause(void); int smf_metrics_free_inst_by_cause(ogs_metrics_inst_t **inst); typedef struct smf_metric_key_by_cause_s { uint8_t cause; smf_metric_type_by_cause_t t; } smf_metric_key_by_cause_t; void smf_metrics_init_by_cause(void) { metrics_hash_by_cause = ogs_hash_make(); ogs_assert(metrics_hash_by_cause); } void smf_metrics_inst_by_cause_add(uint8_t cause, smf_metric_type_by_cause_t t, int val) { ogs_metrics_inst_t *metrics = NULL; smf_metric_key_by_cause_t *cause_key; cause_key = ogs_calloc(1, sizeof(*cause_key)); ogs_assert(cause_key); cause_key->cause = cause; cause_key->t = t; metrics = ogs_hash_get(metrics_hash_by_cause, cause_key, sizeof(*cause_key)); if (!metrics) { char cause_str[4]; ogs_snprintf(cause_str, sizeof(cause_str), "%d", cause); metrics = ogs_metrics_inst_new(smf_metrics_spec_by_cause[t], smf_metrics_spec_def_by_cause->num_labels, (const char *[]){ cause_str }); ogs_assert(metrics); ogs_hash_set(metrics_hash_by_cause, cause_key, sizeof(*cause_key), metrics); } else { ogs_free(cause_key); } ogs_metrics_inst_add(metrics, val); } int smf_metrics_free_inst_by_cause(ogs_metrics_inst_t **inst) { return smf_metrics_free_inst(inst, _SMF_METR_BY_CAUSE_MAX); } int smf_metrics_open(void) { ogs_metrics_context_t *ctx = ogs_metrics_self(); ogs_metrics_context_open(ctx); smf_metrics_init_spec(ctx, smf_metrics_spec_global, smf_metrics_spec_def_global, _SMF_METR_GLOB_MAX); smf_metrics_init_spec(ctx, smf_metrics_spec_gtp_node, smf_metrics_spec_def_gtp_node, _SMF_METR_GTP_NODE_MAX); smf_metrics_init_spec(ctx, smf_metrics_spec_by_slice, smf_metrics_spec_def_by_slice, _SMF_METR_BY_SLICE_MAX); smf_metrics_init_spec(ctx, smf_metrics_spec_by_5qi, smf_metrics_spec_def_by_5qi, _SMF_METR_BY_5QI_MAX); smf_metrics_init_spec(ctx, smf_metrics_spec_by_cause, smf_metrics_spec_def_by_cause, _SMF_METR_BY_CAUSE_MAX); smf_metrics_init_inst_global(); smf_metrics_init_by_slice(); smf_metrics_init_by_5qi(); smf_metrics_init_by_cause(); return 0; } int smf_metrics_close(void) { ogs_hash_index_t *hi; ogs_metrics_context_t *ctx = ogs_metrics_self(); if (metrics_hash_by_slice) { for (hi = ogs_hash_first(metrics_hash_by_slice); hi; hi = ogs_hash_next(hi)) { smf_metric_key_by_slice_t *key = (smf_metric_key_by_slice_t *)ogs_hash_this_key(hi); //void *val = ogs_hash_this_val(hi); ogs_hash_set(metrics_hash_by_slice, key, sizeof(*key), NULL); ogs_free(key); /* don't free val (metric itself) - * it will be free'd by ogs_metrics_context_final() */ //ogs_free(val); } ogs_hash_destroy(metrics_hash_by_slice); } if (metrics_hash_by_5qi) { for (hi = ogs_hash_first(metrics_hash_by_5qi); hi; hi = ogs_hash_next(hi)) { smf_metric_key_by_5qi_t *key = (smf_metric_key_by_5qi_t *)ogs_hash_this_key(hi); //void *val = ogs_hash_this_val(hi); ogs_hash_set(metrics_hash_by_5qi, key, sizeof(*key), NULL); ogs_free(key); /* don't free val (metric itself) - * it will be free'd by ogs_metrics_context_final() */ //ogs_free(val); } ogs_hash_destroy(metrics_hash_by_5qi); } if (metrics_hash_by_cause) { for (hi = ogs_hash_first(metrics_hash_by_cause); hi; hi = ogs_hash_next(hi)) { smf_metric_key_by_cause_t *key = (smf_metric_key_by_cause_t *)ogs_hash_this_key(hi); //void *val = ogs_hash_this_val(hi); ogs_hash_set(metrics_hash_by_cause, key, sizeof(*key), NULL); ogs_free(key); /* don't free val (metric itself) - * it will be free'd by ogs_metrics_context_final() */ //ogs_free(val); } ogs_hash_destroy(metrics_hash_by_cause); } ogs_metrics_context_close(ctx); return OGS_OK; }