[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:
<generation>_<measurement_object_class>_<measurement_family_name>_<metric_name_as_in_TS_128_552>

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
This commit is contained in:
Gaber Stare 2022-12-07 06:31:01 +00:00 committed by Sukchan Lee
parent 3e22059916
commit 4c76a254ee
6 changed files with 268 additions and 1 deletions

View File

@ -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);

View File

@ -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 {

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);