389 lines
14 KiB
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;
|
|
}
|
|
}
|