From 4c76a254ee135d41881d94052334b12ce69401ea Mon Sep 17 00:00:00 2001 From: Gaber Stare Date: Wed, 7 Dec 2022 06:31:01 +0000 Subject: [PATCH] [AMF] Add RM metrics support Expose RM metrics with labels according to ETSI TS 128 552 V16.13.0 in AMF by using hash. The metrics are named respecting the rule: ___ Since slice itself is not unique, the plmnid label is exposed in addition to snssai. RegInitFail is exposed as an alternative to RegInitReq and RegInitSucc counters so cause label can be provided. It counts rejected registrations and rejected authentications. Rejected authentications are counted under label cause="0". Exposed metrics example: -standard gauge: fivegs_amffunction_rm_registeredsubnbr{plmnid="00101",snssai="1000009"} 1 -nonstandard counter: fivegs_amffunction_rm_reginitfail{cause="3"} 4 --- src/amf/context.c | 2 + src/amf/context.h | 4 + src/amf/gmm-sm.c | 26 +++++- src/amf/metrics.c | 214 +++++++++++++++++++++++++++++++++++++++++++++ src/amf/metrics.h | 19 ++++ src/amf/nas-path.c | 4 + 6 files changed, 268 insertions(+), 1 deletion(-) diff --git a/src/amf/context.c b/src/amf/context.c index 01eaba0d6..4e579f0bd 100644 --- a/src/amf/context.c +++ b/src/amf/context.c @@ -1396,6 +1396,8 @@ amf_ue_t *amf_ue_add(ran_ue_t *ran_ue) amf_ue->nas.amf.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; amf_ue->abba_len = 2; + amf_ue->rm_state = RM_STATE_DEREGISTERED; + amf_ue_fsm_init(amf_ue); ogs_list_add(&self.amf_ue_list, amf_ue); diff --git a/src/amf/context.h b/src/amf/context.h index 32403fbba..476377390 100644 --- a/src/amf/context.h +++ b/src/amf/context.h @@ -408,6 +408,10 @@ struct amf_ue_s { char *data_change_subscription_id; ogs_list_t sess_list; + +#define RM_STATE_DEREGISTERED 0 +#define RM_STATE_REGISTERED 1 + uint8_t rm_state; }; typedef struct amf_sess_s { diff --git a/src/amf/gmm-sm.c b/src/amf/gmm-sm.c index af6c98ef7..d9eda57bb 100644 --- a/src/amf/gmm-sm.c +++ b/src/amf/gmm-sm.c @@ -444,7 +444,7 @@ void gmm_state_registered(ogs_fsm_t *s, amf_event_t *e) static void common_register_state(ogs_fsm_t *s, amf_event_t *e) { - int rv, xact_count = 0; + int rv, i, xact_count = 0; ogs_nas_5gmm_cause_t gmm_cause; amf_ue_t *amf_ue = NULL; @@ -644,6 +644,13 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e) gmm_handle_deregistration_request( amf_ue, &nas_message->gmm.deregistration_request_from_ue); + ogs_assert(amf_ue->num_of_slice <= OGS_MAX_NUM_OF_SLICE); + for (i = 0; i < amf_ue->num_of_slice; i++) { + amf_metrics_inst_by_slice_add(&amf_ue->nr_tai.plmn_id, + &amf_ue->slice[i].s_nssai, + AMF_METR_GAUGE_RM_REGISTEREDSUBNBR, -1); + } + amf_ue->rm_state = RM_STATE_DEREGISTERED; OGS_FSM_TRAN(s, &gmm_state_de_registered); break; @@ -659,6 +666,13 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e) NGAP_Cause_PR_misc, NGAP_CauseMisc_om_intervention, NGAP_UE_CTX_REL_NG_CONTEXT_REMOVE, 0)); + ogs_assert(amf_ue->num_of_slice <= OGS_MAX_NUM_OF_SLICE); + for (i = 0; i < amf_ue->num_of_slice; i++) { + amf_metrics_inst_by_slice_add(&amf_ue->nr_tai.plmn_id, + &amf_ue->slice[i].s_nssai, + AMF_METR_GAUGE_RM_REGISTEREDSUBNBR, -1); + } + amf_ue->rm_state = RM_STATE_DEREGISTERED; OGS_FSM_TRAN(s, &gmm_state_de_registered); break; @@ -1340,6 +1354,16 @@ void gmm_state_initial_context_setup(ogs_fsm_t *s, amf_event_t *e) switch (nas_message->gmm.h.message_type) { case OGS_NAS_5GS_REGISTRATION_COMPLETE: ogs_info("[%s] Registration complete", amf_ue->supi); + if (amf_ue->rm_state == RM_STATE_DEREGISTERED){ + int i; + ogs_assert(amf_ue->num_of_slice <= OGS_MAX_NUM_OF_SLICE); + for (i = 0; i < amf_ue->num_of_slice; i++) { + amf_metrics_inst_by_slice_add(&amf_ue->nr_tai.plmn_id, + &amf_ue->slice[i].s_nssai, + AMF_METR_GAUGE_RM_REGISTEREDSUBNBR, 1); + } + } + amf_ue->rm_state = RM_STATE_REGISTERED; CLEAR_AMF_UE_TIMER(amf_ue->t3550); diff --git a/src/amf/metrics.c b/src/amf/metrics.c index cf4af120e..a2518283c 100644 --- a/src/amf/metrics.c +++ b/src/amf/metrics.c @@ -64,6 +64,7 @@ amf_metrics_spec_def_t amf_metrics_spec_def_global[_AMF_METR_GLOB_MAX] = { .name = "gnb", .description = "gNodeBs", }, +/* Global Counters: */ }; int amf_metrics_init_inst_global(void) { @@ -75,6 +76,177 @@ int amf_metrics_free_inst_global(void) return amf_metrics_free_inst(amf_metrics_inst_global, _AMF_METR_GLOB_MAX); } +/* BY SLICE */ +const char *labels_slice[] = { + "plmnid", + "snssai" +}; + +#define AMF_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 *amf_metrics_spec_by_slice[_AMF_METR_BY_SLICE_MAX]; +ogs_hash_t *metrics_hash_by_slice = NULL; /* hash table for SLICE labels */ +amf_metrics_spec_def_t amf_metrics_spec_def_by_slice[_AMF_METR_BY_SLICE_MAX] = { +/* Gauges: */ +AMF_METR_BY_SLICE_GAUGE_ENTRY( + AMF_METR_GAUGE_RM_REGISTEREDSUBNBR, + "fivegs_amffunction_rm_registeredsubnbr", + "Number of registered state subscribers per AMF") +}; +void amf_metrics_init_by_slice(void); +int amf_metrics_free_inst_by_slice(ogs_metrics_inst_t **inst); +typedef struct amf_metric_key_by_slice_s { + ogs_plmn_id_t plmn_id; + ogs_s_nssai_t snssai; + amf_metric_type_by_slice_t t; +} amf_metric_key_by_slice_t; + +void amf_metrics_init_by_slice(void) +{ + metrics_hash_by_slice = ogs_hash_make(); + ogs_assert(metrics_hash_by_slice); +} + +void amf_metrics_inst_by_slice_add(ogs_plmn_id_t *plmn, + ogs_s_nssai_t *snssai, amf_metric_type_by_slice_t t, int val) +{ + ogs_metrics_inst_t *metrics = NULL; + amf_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(amf_metrics_spec_by_slice[t], + amf_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 amf_metrics_free_inst_by_slice(ogs_metrics_inst_t **inst) +{ + return amf_metrics_free_inst(inst, _AMF_METR_BY_SLICE_MAX); +} + +/* BY CAUSE */ +const char *labels_cause[] = { + "cause" +}; + +#define AMF_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 *amf_metrics_spec_by_cause[_AMF_METR_BY_CAUSE_MAX]; +ogs_hash_t *metrics_hash_by_cause = NULL; /* hash table for CAUSE labels */ +amf_metrics_spec_def_t amf_metrics_spec_def_by_cause[_AMF_METR_BY_CAUSE_MAX] = { +/* Counters: */ +AMF_METR_BY_CAUSE_CTR_ENTRY( + AMF_METR_CTR_RM_REG_INITFAIL, + "fivegs_amffunction_rm_reginitfail", + "Number of failed initial registrations at the AMF") +}; +void amf_metrics_init_by_cause(void); +int amf_metrics_free_inst_by_cause(ogs_metrics_inst_t **inst); +typedef struct amf_metric_key_by_cause_s { + uint8_t cause; + amf_metric_type_by_cause_t t; +} amf_metric_key_by_cause_t; + +void amf_metrics_init_by_cause(void) +{ + metrics_hash_by_cause = ogs_hash_make(); + ogs_assert(metrics_hash_by_cause); +} + +void amf_metrics_inst_by_cause_add(uint8_t cause, + amf_metric_type_by_cause_t t, int val) +{ + ogs_metrics_inst_t *metrics = NULL; + amf_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(amf_metrics_spec_by_cause[t], + amf_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 amf_metrics_free_inst_by_cause(ogs_metrics_inst_t **inst) +{ + return amf_metrics_free_inst(inst, _AMF_METR_BY_CAUSE_MAX); +} + int amf_metrics_open(void) { ogs_metrics_context_t *ctx = ogs_metrics_self(); @@ -83,13 +255,55 @@ int amf_metrics_open(void) amf_metrics_init_spec(ctx, amf_metrics_spec_global, amf_metrics_spec_def_global, _AMF_METR_GLOB_MAX); + amf_metrics_init_spec(ctx, amf_metrics_spec_by_slice, + amf_metrics_spec_def_by_slice, _AMF_METR_BY_SLICE_MAX); + amf_metrics_init_spec(ctx, amf_metrics_spec_by_cause, + amf_metrics_spec_def_by_cause, _AMF_METR_BY_CAUSE_MAX); + amf_metrics_init_inst_global(); + + amf_metrics_init_by_slice(); + amf_metrics_init_by_cause(); + return 0; } int amf_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)) { + amf_metric_key_by_slice_t *key = + (amf_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 ifself) - + * it will be free'd by ogs_metrics_context_final() */ + //ogs_free(val); + } + ogs_hash_destroy(metrics_hash_by_slice); + } + if (metrics_hash_by_cause) { + for (hi = ogs_hash_first(metrics_hash_by_cause); hi; hi = ogs_hash_next(hi)) { + amf_metric_key_by_cause_t *key = + (amf_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 ifself) - + * 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/amf/metrics.h b/src/amf/metrics.h index fd9ef735e..8dad1b4fc 100644 --- a/src/amf/metrics.h +++ b/src/amf/metrics.h @@ -27,6 +27,25 @@ static inline void amf_metrics_inst_global_inc(amf_metric_type_global_t t) static inline void amf_metrics_inst_global_dec(amf_metric_type_global_t t) { ogs_metrics_inst_dec(amf_metrics_inst_global[t]); } +/* BY SLICE */ +typedef enum amf_metric_type_by_slice_s { + AMF_METR_GAUGE_RM_REGISTEREDSUBNBR = 0, + _AMF_METR_BY_SLICE_MAX, +} amf_metric_type_by_slice_t; + +void amf_metrics_inst_by_slice_add( + ogs_plmn_id_t *plmn, ogs_s_nssai_t *snssai, + amf_metric_type_by_slice_t t, int val); + +/* BY CAUSE */ +typedef enum amf_metric_type_by_cause_s { + AMF_METR_CTR_RM_REG_INITFAIL = 0, + _AMF_METR_BY_CAUSE_MAX, +} amf_metric_type_by_cause_t; + +void amf_metrics_inst_by_cause_add( + uint8_t cause, amf_metric_type_by_cause_t t, int val); + int amf_metrics_open(void); int amf_metrics_close(void); diff --git a/src/amf/nas-path.c b/src/amf/nas-path.c index a30420350..faf7c5faf 100644 --- a/src/amf/nas-path.c +++ b/src/amf/nas-path.c @@ -158,6 +158,8 @@ int nas_5gs_send_registration_reject( int rv; ogs_pkbuf_t *gmmbuf = NULL; + amf_metrics_inst_by_cause_add(gmm_cause, AMF_METR_CTR_RM_REG_INITFAIL, 1); + ogs_assert(amf_ue); ogs_warn("[%s] Registration reject [%d]", amf_ue->suci, gmm_cause); @@ -379,6 +381,8 @@ int nas_5gs_send_authentication_reject(amf_ue_t *amf_ue) int rv; ogs_pkbuf_t *gmmbuf = NULL; + amf_metrics_inst_by_cause_add(0, AMF_METR_CTR_RM_REG_INITFAIL, 1); + ogs_assert(amf_ue); ogs_warn("[%s] Authentication reject", amf_ue->suci);