From 3edceecf66b5642753f04a865c1fcf7129ed0a5d Mon Sep 17 00:00:00 2001 From: Gaber Stare Date: Wed, 7 Dec 2022 07:44:57 +0000 Subject: [PATCH] [SMF] Add SM metrics support Expose SM metrics with labels according to ETSI TS 128 552 V16.13.0 in SMF by using hash. The metrics are named respecting the rule: ___ Existing gauge sessions_active is renamed! Since slice itself is not unique, the plmnid label is exposed in addition to snssai. Exposed metrics example: -standard gauges: fivegs_smffunction_sm_sessionnbr{plmnid="00101",snssai="1000009"} 0 fivegs_smffunction_sm_qos_flow_nbr{plmnid="00101",snssai="1000009",fiveqi="9"} 0 -nonstandard counters: fivegs_smffunction_sm_n4sessionestabfail{cause="71"} 68 fivegs_smffunction_sm_n4sessionreport 1 fivegs_smffunction_sm_n4sessionreportsucc 1 fivegs_smffunction_sm_n4sessionestabreq 1 --- src/smf/context.c | 20 ++- src/smf/metrics.c | 353 ++++++++++++++++++++++++++++++++++++++++- src/smf/metrics.h | 32 +++- src/smf/n4-handler.c | 5 + src/smf/nsmf-handler.c | 3 + 5 files changed, 402 insertions(+), 11 deletions(-) diff --git a/src/smf/context.c b/src/smf/context.c index 9736d0fbc..60bd34e32 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -39,7 +39,7 @@ static int context_initialized = 0; static int num_of_smf_sess = 0; static void stats_add_smf_session(void); -static void stats_remove_smf_session(void); +static void stats_remove_smf_session(smf_sess_t *sess); int smf_ctf_config_init(smf_ctf_config_t *ctf_config) { @@ -1797,7 +1797,7 @@ void smf_sess_remove(smf_sess_t *sess) smf_metrics_inst_global_dec(SMF_METR_GLOB_GAUGE_GTP2_SESSIONS_ACTIVE); break; } - stats_remove_smf_session(); + stats_remove_smf_session(sess); ogs_pool_free(&smf_sess_pool, sess); } @@ -2028,6 +2028,8 @@ smf_bearer_t *smf_qos_flow_add(smf_sess_t *sess) qos_flow->sess = sess; ogs_list_add(&sess->bearer_list, qos_flow); + smf_metrics_inst_by_5qi_add(&sess->plmn_id, &sess->s_nssai, + sess->session.qos.index, SMF_METR_GAUGE_SM_QOSFLOWNBR, 1); return qos_flow; } @@ -2438,6 +2440,10 @@ int smf_bearer_remove(smf_bearer_t *bearer) ogs_assert(bearer); ogs_assert(bearer->sess); + smf_metrics_inst_by_5qi_add(&bearer->sess->plmn_id, + &bearer->sess->s_nssai, bearer->sess->session.qos.index, + SMF_METR_GAUGE_SM_QOSFLOWNBR, -1); + ogs_list_remove(&bearer->sess->bearer_list, bearer); ogs_assert(bearer->dl_pdr); @@ -3081,14 +3087,18 @@ void smf_pf_precedence_pool_final(smf_sess_t *sess) static void stats_add_smf_session(void) { - smf_metrics_inst_global_inc(SMF_METR_GLOB_GAUGE_SESSIONS_ACTIVE); num_of_smf_sess = num_of_smf_sess + 1; ogs_info("[Added] Number of SMF-Sessions is now %d", num_of_smf_sess); } -static void stats_remove_smf_session(void) +static void stats_remove_smf_session(smf_sess_t *sess) { - smf_metrics_inst_global_dec(SMF_METR_GLOB_GAUGE_SESSIONS_ACTIVE); + ogs_assert(sess); + + if (sess->s_nssai.sst != 0) { + smf_metrics_inst_by_slice_add(&sess->plmn_id, &sess->s_nssai, + SMF_METR_GAUGE_SM_SESSIONNBR, -1); + } num_of_smf_sess = num_of_smf_sess - 1; ogs_info("[Removed] Number of SMF-Sessions is now %d", num_of_smf_sess); } diff --git a/src/smf/metrics.c b/src/smf/metrics.c index 0604ad6c5..0c23b0c9d 100644 --- a/src/smf/metrics.c +++ b/src/smf/metrics.c @@ -84,17 +84,27 @@ smf_metrics_spec_def_t smf_metrics_spec_def_global[_SMF_METR_GLOB_MAX] = { .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_SESSIONS_ACTIVE] = { - .type = OGS_METRICS_METRIC_TYPE_GAUGE, - .name = "sessions_active", - .description = "Active Sessions", -}, [SMF_METR_GLOB_GAUGE_BEARERS_ACTIVE] = { .type = OGS_METRICS_METRIC_TYPE_GAUGE, .name = "bearers_active", @@ -177,6 +187,281 @@ 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(); @@ -188,13 +473,71 @@ int smf_metrics_open(void) 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; } diff --git a/src/smf/metrics.h b/src/smf/metrics.h index 94f75810d..ad7c03746 100644 --- a/src/smf/metrics.h +++ b/src/smf/metrics.h @@ -16,8 +16,10 @@ typedef enum smf_metric_type_global_s { SMF_METR_GLOB_CTR_S5C_RX_PARSE_FAILED, SMF_METR_GLOB_CTR_S5C_RX_CREATESESSIONREQ, SMF_METR_GLOB_CTR_S5C_RX_DELETESESSIONREQ, + SMF_METR_GLOB_CTR_SM_N4SESSIONESTABREQ, + SMF_METR_GLOB_CTR_SM_N4SESSIONREPORT, + SMF_METR_GLOB_CTR_SM_N4SESSIONREPORTSUCC, SMF_METR_GLOB_GAUGE_UES_ACTIVE, - SMF_METR_GLOB_GAUGE_SESSIONS_ACTIVE, SMF_METR_GLOB_GAUGE_BEARERS_ACTIVE, SMF_METR_GLOB_GAUGE_GTP1_PDPCTXS_ACTIVE, SMF_METR_GLOB_GAUGE_GTP2_SESSIONS_ACTIVE, @@ -63,6 +65,34 @@ static inline void smf_metrics_inst_gtp_node_dec( ogs_metrics_inst_t **inst, smf_metric_type_gtp_node_t t) { ogs_metrics_inst_dec(inst[t]); } +/* BY SLICE */ +typedef enum smf_metric_type_by_slice_s { + SMF_METR_GAUGE_SM_SESSIONNBR = 0, + _SMF_METR_BY_SLICE_MAX, +} smf_metric_type_by_slice_t; + +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); + +/* BY SLICE and 5QI */ +typedef enum smf_metric_type_by_5qi_s { + SMF_METR_GAUGE_SM_QOSFLOWNBR = 0, + _SMF_METR_BY_5QI_MAX, +} smf_metric_type_by_5qi_t; + +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); + +/* BY CAUSE */ +typedef enum smf_metric_type_by_cause_s { + SMF_METR_CTR_SM_N4SESSIONESTABFAIL = 0, + _SMF_METR_BY_CAUSE_MAX, +} smf_metric_type_by_cause_t; + +void smf_metrics_inst_by_cause_add( + uint8_t cause, smf_metric_type_by_cause_t t, int val); int smf_metrics_open(void); int smf_metrics_close(void); diff --git a/src/smf/n4-handler.c b/src/smf/n4-handler.c index cf4d0fb89..b345d8923 100644 --- a/src/smf/n4-handler.c +++ b/src/smf/n4-handler.c @@ -178,6 +178,8 @@ uint8_t smf_5gc_n4_handle_session_establishment_response( if (rsp->cause.u8 != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { ogs_error("PFCP Cause [%d] : Not Accepted", rsp->cause.u8); cause_value = rsp->cause.u8; + smf_metrics_inst_by_cause_add(cause_value, + SMF_METR_CTR_SM_N4SESSIONESTABFAIL, 1); } } else { ogs_error("No Cause"); @@ -1146,6 +1148,8 @@ void smf_n4_handle_session_report_request( uint16_t pdr_id = 0; unsigned int i; + smf_metrics_inst_global_inc(SMF_METR_GLOB_CTR_SM_N4SESSIONREPORT); + ogs_assert(pfcp_xact); ogs_assert(pfcp_req); @@ -1321,6 +1325,7 @@ void smf_n4_handle_session_report_request( ogs_assert(OGS_OK == smf_pfcp_send_session_report_response( pfcp_xact, sess, OGS_PFCP_CAUSE_REQUEST_ACCEPTED)); + smf_metrics_inst_global_inc(SMF_METR_GLOB_CTR_SM_N4SESSIONREPORTSUCC); } else { ogs_error("Not supported Report Type[%d]", report_type.value); ogs_assert(OGS_OK == diff --git a/src/smf/nsmf-handler.c b/src/smf/nsmf-handler.c index b2143933c..2306ea2d3 100644 --- a/src/smf/nsmf-handler.c +++ b/src/smf/nsmf-handler.c @@ -181,6 +181,9 @@ bool smf_nsmf_handle_create_sm_context( SmContextCreateData->hplmn_snssai->sd); } + smf_metrics_inst_by_slice_add(&sess->plmn_id, &sess->s_nssai, + SMF_METR_GAUGE_SM_SESSIONNBR, 1); + if (sess->sm_context_status_uri) ogs_free(sess->sm_context_status_uri); sess->sm_context_status_uri =