osmo-hnbgw/src/osmo-hnbgw/kpi_ranap.c

389 lines
14 KiB
C

/* KPI (statistics, counters) at RANAP level */
/* (C) 2024 by Harald Welte <laforge@osmocom.org>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <osmocom/core/utils.h>
#include <osmocom/ranap/ranap_common_ran.h>
#include <osmocom/hnbgw/hnbgw_cn.h>
#include <osmocom/hnbgw/context_map.h>
#include <osmocom/hnbgw/kpi.h>
/***********************************************************************
* DOWNLINK messages
***********************************************************************/
static void kpi_ranap_process_dl_iu_rel_cmd(struct hnbgw_context_map *map, const ranap_message *ranap)
{
struct hnb_persistent *hnbp = map->hnb_ctx->persistent;
const RANAP_Cause_t *cause;
OSMO_ASSERT(ranap->procedureCode == RANAP_ProcedureCode_id_Iu_Release);
cause = &ranap->msg.iu_ReleaseCommandIEs.cause;
/* When Iu is released, all RABs are released implicitly */
for (unsigned int i = 0; i < ARRAY_SIZE(map->rab_state); i++) {
unsigned int ctr_num;
switch (map->rab_state[i]) {
case RAB_STATE_ACTIVE:
if (cause->present == RANAP_Cause_PR_nAS ||
cause->choice.nAS == RANAP_CauseNAS_normal_release) {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT;
} else {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL;
}
if (!map->is_ps)
ctr_num++;
HNBP_CTR_INC(hnbp, ctr_num);
break;
}
}
/* clear all RAB state */
memset(map->rab_state, 0, sizeof(map->rab_state));
}
static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, ranap_message *ranap)
{
struct hnb_persistent *hnbp = map->hnb_ctx->persistent;
RANAP_RAB_AssignmentRequestIEs_t *ies;
int rc;
OSMO_ASSERT(ranap->procedureCode == RANAP_ProcedureCode_id_RAB_Assignment);
ies = &ranap->msg.raB_AssignmentRequestIEs;
if (ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_SETUPORMODIFYLIST_PRESENT) {
RANAP_RAB_SetupOrModifyList_t *som_list = &ies->raB_SetupOrModifyList;
for (unsigned int i = 0; i < som_list->list.count; i++) {
RANAP_ProtocolIE_ContainerPair_t *container_pair = som_list->list.array[i];
RANAP_ProtocolIE_FieldPair_t *field_pair = container_pair->list.array[0];
RANAP_RAB_SetupOrModifyItemFirst_t _rab_setup_or_modify_item_first = {};
RANAP_RAB_SetupOrModifyItemFirst_t *rab_setup_or_modify_item_first = &_rab_setup_or_modify_item_first;
uint8_t rab_id;
if (!field_pair)
continue;
if (field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem)
continue;
rc = ranap_decode_rab_setupormodifyitemfirst(rab_setup_or_modify_item_first, &field_pair->firstValue);
if (rc < 0)
continue;
/* RAB-ID is an 8-bit bit-string, so it's the first byte */
rab_id = rab_setup_or_modify_item_first->rAB_ID.buf[0];
/* the only way to distinguish a "setup" from a "modify" is to know which RABs are
* already established. If it's already established, it is a modification; if it's
* new, it is a setup */
switch (map->rab_state[rab_id]) {
case RAB_STATE_ACTIVE:
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_REQ : HNB_CTR_RANAP_CS_RAB_MOD_REQ);
break;
case RAB_STATE_INACTIVE:
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_REQ : HNB_CTR_RANAP_CS_RAB_ACT_REQ);
map->rab_state[rab_id] = RAB_STATE_ACT_REQ;
break;
default:
LOG_MAP(map, DRANAP, LOGL_NOTICE,
"Unexpected RAB Activation/Modification Req for RAB in state %u\n", map->rab_state[rab_id]);
break;
}
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, rab_setup_or_modify_item_first);
}
}
if (ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_RELEASELIST_PRESENT) {
RANAP_RAB_ReleaseList_t *r_list = &ies->raB_ReleaseList;
for (unsigned int i = 0; i < r_list->raB_ReleaseList_ies.list.count; i++) {
RANAP_IE_t *release_list_ie = r_list->raB_ReleaseList_ies.list.array[i];
RANAP_RAB_ReleaseItemIEs_t _rab_rel_item_ies = {};
RANAP_RAB_ReleaseItemIEs_t *rab_rel_item_ies = &_rab_rel_item_ies;
RANAP_RAB_ReleaseItem_t *rab_rel_item;
unsigned int ctr_num;
uint8_t rab_id;
if (!release_list_ie)
continue;
if (release_list_ie->id != RANAP_ProtocolIE_ID_id_RAB_ReleaseItem)
continue;
rc = ranap_decode_rab_releaseitemies_fromlist(rab_rel_item_ies, &release_list_ie->value);
if (rc < 0)
continue;
rab_rel_item = &rab_rel_item_ies->raB_ReleaseItem;
/* RAB-ID is an 8-bit bit-string, so it's the first byte */
rab_id = rab_rel_item->rAB_ID.buf[0];
switch (map->rab_state[rab_id]) {
case RAB_STATE_ACTIVE:
if (rab_rel_item->cause.present == RANAP_Cause_PR_nAS &&
rab_rel_item->cause.choice.nAS == RANAP_CauseNAS_normal_release) {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_REQ;
} else {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL;
}
if (!map->is_ps)
ctr_num++;
HNBP_CTR_INC(hnbp, ctr_num);
break;
default:
LOG_MAP(map, DRANAP, LOGL_NOTICE,
"Unexpected RAB Release Req in state %u\n", map->rab_state[rab_id]);
break;
}
/* mark that RAB as release requested */
map->rab_state[rab_id] = RAB_STATE_REL_REQ;
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_ReleaseItem, rab_rel_item_ies);
}
}
}
void kpi_ranap_process_dl(struct hnbgw_context_map *map, ranap_message *ranap)
{
switch (ranap->procedureCode) {
case RANAP_ProcedureCode_id_RAB_Assignment: /* RAB ASSIGNMENT REQ (8.2) */
kpi_ranap_process_dl_rab_ass_req(map, ranap);
break;
case RANAP_ProcedureCode_id_Iu_Release:
kpi_ranap_process_dl_iu_rel_cmd(map, ranap); /* IU RELEASE CMD (8.5) */
break;
default:
break;
}
}
/***********************************************************************
* UPLINK messages
***********************************************************************/
static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ranap_message *ranap)
{
struct hnb_persistent *hnbp = map->hnb_ctx->persistent;
RANAP_RAB_AssignmentResponseIEs_t *ies;
int rc;
OSMO_ASSERT(ranap->procedureCode == RANAP_ProcedureCode_id_RAB_Assignment);
ies = &ranap->msg.raB_AssignmentResponseIEs;
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT) {
RANAP_RAB_SetupOrModifiedList_t *som_list = &ies->raB_SetupOrModifiedList;
for (unsigned int i = 0; i < som_list->raB_SetupOrModifiedList_ies.list.count; i++) {
RANAP_IE_t *som_list_ie = som_list->raB_SetupOrModifiedList_ies.list.array[i];
RANAP_RAB_SetupOrModifiedItemIEs_t _rab_som_item_ies = {};
RANAP_RAB_SetupOrModifiedItemIEs_t *rab_som_item_ies = &_rab_som_item_ies;
RANAP_RAB_SetupOrModifiedItem_t *rab_som_item;
uint8_t rab_id;
if (!som_list_ie)
continue;
if (som_list_ie->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifiedItem)
continue;
rc = ranap_decode_rab_setupormodifieditemies_fromlist(rab_som_item_ies, &som_list_ie->value);
if (rc < 0)
continue;
rab_som_item = &rab_som_item_ies->raB_SetupOrModifiedItem;
/* RAB-ID is an 8-bit bit-string, so it's the first byte */
rab_id = rab_som_item->rAB_ID.buf[0];
/* differentiate modify / activate */
switch (map->rab_state[rab_id]) {
case RAB_STATE_ACT_REQ:
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_CNF : HNB_CTR_RANAP_CS_RAB_ACT_CNF);
map->rab_state[rab_id] = RAB_STATE_ACTIVE;
break;
case RAB_STATE_ACTIVE:
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_CNF : HNB_CTR_RANAP_CS_RAB_MOD_CNF);
break;
default:
LOG_MAP(map, DRANAP, LOGL_NOTICE,
"Unexpected RAB Activation/Modification Conf for RAB in state %u\n", map->rab_state[rab_id]);
break;
}
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, rab_som_item_ies);
}
}
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_RELEASEDLIST_PRESENT) {
RANAP_RAB_ReleasedList_t *r_list = &ies->raB_ReleasedList;
/* increment number of released RABs, we don't need to do that individually during iteration */
HNBP_CTR_ADD(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_CNF : HNB_CTR_RANAP_CS_RAB_REL_CNF,
r_list->raB_ReleasedList_ies.list.count);
for (unsigned int i = 0; i < r_list->raB_ReleasedList_ies.list.count; i++) {
RANAP_IE_t *released_list_ie = r_list->raB_ReleasedList_ies.list.array[i];
RANAP_RAB_ReleasedItemIEs_t _rab_rel_item_ies = {};
RANAP_RAB_ReleasedItemIEs_t *rab_rel_item_ies = &_rab_rel_item_ies;
RANAP_RAB_ReleasedItem_t *rab_rel_item;
uint8_t rab_id;
if (!released_list_ie)
continue;
if (released_list_ie->id != RANAP_ProtocolIE_ID_id_RAB_ReleasedItem)
continue;
rc = ranap_decode_rab_releaseditemies_fromlist(rab_rel_item_ies, &released_list_ie->value);
if (rc < 0)
continue;
rab_rel_item = &rab_rel_item_ies->raB_ReleasedItem;
/* RAB-ID is an 8-bit bit-string, so it's the first byte */
rab_id = rab_rel_item->rAB_ID.buf[0];
switch (map->rab_state[rab_id]) {
case RAB_STATE_REL_REQ:
break;
default:
LOG_MAP(map, DRANAP, LOGL_NOTICE,
"Unexpected RAB Release Conf for RAB in state %u\n", map->rab_state[rab_id]);
break;
}
/* mark that RAB as released */
map->rab_state[rab_id] = RAB_STATE_INACTIVE;
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_ReleasedItem, rab_rel_item_ies);
}
}
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_QUEUEDLIST_PRESENT)
LOG_MAP(map, DRANAP, LOGL_NOTICE, "RAB Activation has been queued; we don't support KPIs for this\n");
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_FAILEDLIST_PRESENT) {
RANAP_RAB_FailedList_t *f_list = &ies->raB_FailedList;
for (unsigned int i = 0; i < f_list->raB_FailedList_ies.list.count; i++) {
RANAP_IE_t *failed_list_ie = f_list->raB_FailedList_ies.list.array[i];
RANAP_RAB_FailedItemIEs_t _rab_failed_item_ies = {};
RANAP_RAB_FailedItemIEs_t *rab_failed_item_ies = &_rab_failed_item_ies;
RANAP_RAB_FailedItem_t *rab_failed_item;
uint8_t rab_id;
if (!failed_list_ie)
continue;
if (failed_list_ie->id != RANAP_ProtocolIE_ID_id_RAB_FailedItem)
continue;
rc = ranap_decode_rab_faileditemies_fromlist(rab_failed_item_ies, &failed_list_ie->value);
if (rc < 0)
continue;
rab_failed_item = &rab_failed_item_ies->raB_FailedItem;
/* RAB-ID is an 8-bit bit-string, so it's the first byte */
rab_id = rab_failed_item->rAB_ID.buf[0];
/* differentiate modify / activate */
switch (map->rab_state[rab_id]) {
case RAB_STATE_ACT_REQ:
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_FAIL : HNB_CTR_RANAP_CS_RAB_ACT_FAIL);
map->rab_state[rab_id] = RAB_STATE_INACTIVE;
break;
case RAB_STATE_ACTIVE:
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_FAIL : HNB_CTR_RANAP_CS_RAB_MOD_FAIL);
// FIXME: does it remain active after modification failure?
break;
default:
LOG_MAP(map, DRANAP, LOGL_NOTICE,
"Unexpected RAB Activation/Modification Failed for RAB in state %u\n", map->rab_state[rab_id]);
break;
}
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_FailedItem, rab_failed_item_ies);
}
}
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_RELEASEFAILEDLIST_PRESENT) {
RANAP_RAB_ReleaseFailedList_t *rf_list = &ies->raB_ReleaseFailedList;
/* increment number of released RABs, we don't need to do that individually during iteration */
HNBP_CTR_ADD(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_FAIL : HNB_CTR_RANAP_CS_RAB_REL_FAIL,
rf_list->raB_FailedList_ies.list.count);
for (unsigned int i = 0; i < rf_list->raB_FailedList_ies.list.count; i++) {
RANAP_IE_t *failed_list_ie = rf_list->raB_FailedList_ies.list.array[i];
RANAP_RAB_FailedItemIEs_t _rab_failed_item_ies = {};
RANAP_RAB_FailedItemIEs_t *rab_failed_item_ies = &_rab_failed_item_ies;
RANAP_RAB_FailedItem_t *rab_failed_item;
uint8_t rab_id;
if (!failed_list_ie)
continue;
if (failed_list_ie->id != RANAP_ProtocolIE_ID_id_RAB_FailedItem)
continue;
rc = ranap_decode_rab_faileditemies_fromlist(rab_failed_item_ies, &failed_list_ie->value);
if (rc < 0)
continue;
rab_failed_item = &rab_failed_item_ies->raB_FailedItem;
/* RAB-ID is an 8-bit bit-string, so it's the first byte */
rab_id = rab_failed_item->rAB_ID.buf[0];
/* differentiate modify / activate */
switch (map->rab_state[rab_id]) {
case RAB_STATE_ACT_REQ:
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_FAIL : HNB_CTR_RANAP_CS_RAB_ACT_FAIL);
map->rab_state[rab_id] = RAB_STATE_INACTIVE;
break;
case RAB_STATE_ACTIVE:
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_FAIL : HNB_CTR_RANAP_CS_RAB_MOD_FAIL);
// FIXME: does it remain active after modification failure?
break;
default:
LOG_MAP(map, DRANAP, LOGL_NOTICE,
"Unexpected RAB Release Failed for RAB in state %u\n", map->rab_state[rab_id]);
break;
}
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_FailedItem, rab_failed_item_ies);
}
}
}
void kpi_ranap_process_ul(struct hnbgw_context_map *map, ranap_message *ranap)
{
switch (ranap->procedureCode) {
case RANAP_ProcedureCode_id_RAB_Assignment: /* RAB ASSIGNMENT REQ (8.2) */
kpi_ranap_process_ul_rab_ass_resp(map, ranap);
break;
case RANAP_ProcedureCode_id_Iu_Release:
/* TODO: We might want to parse the list of released RABs here and then mark each of those as
* released. For now we simply assume that all RABs are released in IU RELEASE during
* processing of the downlink Iu Release Command. It's not like the RNC/HNB has any way to
* refuse the release anyway. */
break;
default:
break;
}
}