diff --git a/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt b/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt index 3cb3d062f4..1d47f029d9 100644 --- a/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt +++ b/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt @@ -1,43 +1,49 @@ add_library(e2_ran_func_cuup STATIC init_ran_func.c read_setup_ran.c - ../flexric/test/rnd/fill_rnd_data_e2_setup_req.c # this is not rnd data; it is used to fill E2 Node Component Configuration Addition List in E2 Setup Request message - CUSTOMIZED/ran_func_gtp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP - CUSTOMIZED/ran_func_pdcp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP + O-RAN/ran_e2sm_ue_id.c + O-RAN/ran_func_kpm.c + O-RAN/ran_func_kpm_subs.c + O-RAN/ran_func_rc.c + O-RAN/ran_func_rc_subs.c + CUSTOMIZED/ran_func_gtp.c # GTP SM not yet implemented in CU-UP + CUSTOMIZED/ran_func_pdcp.c CUSTOMIZED/ran_func_tc.c # currently, not implemented; therefore, filling rnd data ../flexric/test/rnd/fill_rnd_data_tc.c - O-RAN/ran_func_kpm.c # this file should only contain PDCP-U/GTP; to be done in the future - ../flexric/test/rnd/fill_rnd_data_kpm.c # this dependancy will be taken out once RAN Function Definition is implemented - O-RAN/ran_func_rc.c # this file should only contain PDCP-U/GTP; to be done in the future - O-RAN/ran_func_rc_subs.c - ../flexric/test/rnd/fill_rnd_data_rc.c # this dependancy will be taken out once RAN Function Definition is implemented ) -target_link_libraries(e2_ran_func_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj) +target_link_libraries(e2_ran_func_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj sm_common_ie_obj) target_compile_definitions(e2_ran_func_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_CUUP) add_library(e2_ran_func_du_cucp_cuup STATIC init_ran_func.c read_setup_ran.c - ../flexric/test/rnd/fill_rnd_data_e2_setup_req.c # this is not rnd data; it is used to fill E2 Node Component Configuration Addition List in E2 Setup Request message - CUSTOMIZED/ran_func_gtp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP - CUSTOMIZED/ran_func_pdcp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP - O-RAN/ran_func_kpm.c # this file should only contain RRC/PDCP-C; to be done in the future - # when nr-softmodem is divided in separate executables - ../flexric/test/rnd/fill_rnd_data_kpm.c # this dependancy will be taken out once RAN Function Definition is implemented - O-RAN/ran_func_rc.c # this file should only contain RRC/PDCP-C; to be done in the future - # when nr-softmodem is divided in separate executables + O-RAN/ran_e2sm_ue_id.c + O-RAN/ran_func_kpm.c + O-RAN/ran_func_kpm_subs.c + O-RAN/ran_func_rc.c O-RAN/ran_func_rc_subs.c - ../flexric/test/rnd/fill_rnd_data_rc.c # this dependancy will be taken out once RAN Function Definition is implemented ../flexric/src/sm/rc_sm/ie/rc_data_ie.c + CUSTOMIZED/ran_func_gtp.c + CUSTOMIZED/ran_func_pdcp.c CUSTOMIZED/ran_func_mac.c CUSTOMIZED/ran_func_rlc.c - CUSTOMIZED/ran_func_slice.c - CUSTOMIZED/ran_func_tc.c - ../flexric/test/rnd/fill_rnd_data_tc.c + CUSTOMIZED/ran_func_slice.c # currently, not implemented; therefore, filling rnd data ../flexric/test/rnd/fill_rnd_data_slice.c + CUSTOMIZED/ran_func_tc.c # currently, not implemented; therefore, filling rnd data + ../flexric/test/rnd/fill_rnd_data_tc.c ) -target_link_libraries(e2_ran_func_du_cucp_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj e2_agent) -target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_DU NGRAN_GNB_CUCP NGRAN_GNB_CUUP) +target_link_libraries(e2_ran_func_du_cucp_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj) +target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_DU NGRAN_GNB_CUCP NGRAN_GNB_CUUP) + +# Current implementation: +# - we only have one executable nr-softmodem for 4 node types: DU, CU, gNB-mono, CU-CP + +# In the future, when nr-softmodem is separated, linking should be done as following: +# - DU: e2_ran_func_du +# - CU-CP : e2_ran_func_cucp +# - CU-UP: e2_ran_func_cuup (this is available at the moment, executable nr-cuup) +# - CU: e2_ran_func_cucp + e2_ran_func_cuup +# - gNB-mono: e2_ran_func_du + e2_ran_func_cucp + e2_ran_func_cuup diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c new file mode 100644 index 0000000000..c3f6277f6f --- /dev/null +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c @@ -0,0 +1,143 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "ran_e2sm_ue_id.h" + +ue_id_e2sm_t fill_e2sm_gnb_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, __attribute__((unused))const uint32_t rrc_ue_id, __attribute__((unused))const ue_id_t cucp_ue_id) +{ + ue_id_e2sm_t ue_id = {0}; + + ue_id.type = GNB_UE_ID_E2SM; + + // 6.2.3.16 + // Mandatory + // AMF UE NGAP ID + ue_id.gnb.amf_ue_ngap_id = rrc_ue_context->amf_ue_ngap_id; + + // Mandatory + //GUAMI 6.2.3.17 + ue_id.gnb.guami.plmn_id = (e2sm_plmn_t) { + .mcc = rrc_ue_context->ue_guami.mcc, + .mnc = rrc_ue_context->ue_guami.mnc, + .mnc_digit_len = rrc_ue_context->ue_guami.mnc_len + }; + + ue_id.gnb.guami.amf_region_id = rrc_ue_context->ue_guami.amf_region_id; + ue_id.gnb.guami.amf_set_id = rrc_ue_context->ue_guami.amf_set_id; + ue_id.gnb.guami.amf_ptr = rrc_ue_context->ue_guami.amf_pointer; + + // RAN UE ID + // Optional + // 6.2.3.25 + // OCTET STRING (SIZE (8)) + // Defined in TS 38.473 (F1AP) + // clause 9.2.2.1 + // UE CONTEXT SETUP REQUEST + ue_id.gnb.ran_ue_id = calloc(1, sizeof(uint64_t)); + assert(ue_id.gnb.ran_ue_id != NULL); + *ue_id.gnb.ran_ue_id = rrc_ue_context->rrc_ue_id; + + return ue_id; +} + +ue_id_e2sm_t fill_e2sm_cu_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id) +{ + ue_id_e2sm_t ue_id = fill_e2sm_gnb_ue_id_data(rrc_ue_context, rrc_ue_id, cucp_ue_id); + + // gNB-CU UE F1AP ID List + // C-ifCUDUseparated + ue_id.gnb.gnb_cu_ue_f1ap_lst_len = 1; + ue_id.gnb.gnb_cu_ue_f1ap_lst = calloc(ue_id.gnb.gnb_cu_ue_f1ap_lst_len, sizeof(uint32_t)); + assert(ue_id.gnb.gnb_cu_ue_f1ap_lst != NULL && "Memory exhausted"); + ue_id.gnb.gnb_cu_ue_f1ap_lst[0] = rrc_ue_context->rrc_ue_id; + + return ue_id; +} + +ue_id_e2sm_t fill_e2sm_du_ue_id_data(__attribute__((unused))const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, __attribute__((unused))const ue_id_t cucp_ue_id) +{ + ue_id_e2sm_t ue_id = {0}; + + ue_id.type = GNB_DU_UE_ID_E2SM; + + // 6.2.3.21 + // gNB CU UE F1AP + // Mandatory + ue_id.gnb_du.gnb_cu_ue_f1ap = rrc_ue_id; + + // 6.2.3.25 + // RAN UE ID + // Optional + ue_id.gnb_du.ran_ue_id = calloc(1, sizeof(uint64_t)); + assert(ue_id.gnb_du.ran_ue_id != NULL); + *ue_id.gnb_du.ran_ue_id = rrc_ue_id; + + return ue_id; +} + +ue_id_e2sm_t fill_e2sm_cucp_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id) +{ + ue_id_e2sm_t ue_id = fill_e2sm_gnb_ue_id_data(rrc_ue_context, rrc_ue_id, cucp_ue_id); + + //gNB-CU-CP UE E1AP ID List + //C-ifCPUPseparated + ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len = 1; + ue_id.gnb.gnb_cu_cp_ue_e1ap_lst = calloc(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len, sizeof(uint32_t)); + assert(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst != NULL && "Memory exhausted"); + ue_id.gnb.gnb_cu_cp_ue_e1ap_lst[0] = rrc_ue_context->rrc_ue_id; + + return ue_id; +} + +ue_id_e2sm_t fill_e2sm_cuup_ue_id_data(__attribute__((unused))const gNB_RRC_UE_t *rrc_ue_context, __attribute__((unused))const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id) +{ + ue_id_e2sm_t ue_id = {0}; + + ue_id.type = GNB_CU_UP_UE_ID_E2SM; + + // 6.2.3.20 + // Mandatory + ue_id.gnb_cu_up.gnb_cu_cp_ue_e1ap = cucp_ue_id; // RAN UE NGAP ID = gNB UE NGAP ID = rrc_ue_id + + // 6.2.3.25 + // RAN UE ID + // Optional + ue_id.gnb_cu_up.ran_ue_id = calloc(1, sizeof(uint64_t)); + assert(ue_id.gnb_cu_up.ran_ue_id != NULL); + *ue_id.gnb_cu_up.ran_ue_id = cucp_ue_id; // RAN UE NGAP ID = gNB UE NGAP ID = rrc_ue_id + + return ue_id; +} + +get_ue_id fill_ue_id_data[END_NGRAN_NODE_TYPE] = +{ + NULL, + NULL, + fill_e2sm_gnb_ue_id_data, + NULL, + NULL, + fill_e2sm_cu_ue_id_data, + NULL, + fill_e2sm_du_ue_id_data, + NULL, + fill_e2sm_cucp_ue_id_data, + fill_e2sm_cuup_ue_id_data, +}; diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.h b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.h new file mode 100644 index 0000000000..769ff12673 --- /dev/null +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.h @@ -0,0 +1,42 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef RAN_E2SM_UE_ID_H +#define RAN_E2SM_UE_ID_H + +#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h" +#include "openair2/RRC/NR/rrc_gNB_UE_context.h" + +ue_id_e2sm_t fill_e2sm_gnb_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id); + +ue_id_e2sm_t fill_e2sm_cu_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id); + +ue_id_e2sm_t fill_e2sm_du_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id); + +ue_id_e2sm_t fill_e2sm_cucp_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id); + +ue_id_e2sm_t fill_e2sm_cuup_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id); + +typedef ue_id_e2sm_t (*get_ue_id)(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id); + +extern get_ue_id fill_ue_id_data[END_NGRAN_NODE_TYPE]; + +#endif diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c index 357a82b277..1a3099bbaf 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c @@ -20,39 +20,162 @@ */ #include "ran_func_kpm.h" -#include "openair2/E2AP/flexric/test/rnd/fill_rnd_data_kpm.h" // this dependancy will be taken out once RAN Function Definition is implemented -#include "openair2/E2AP/flexric/src/util/time_now_us.h" -#include "common/utils/assertions.h" +#include "ran_func_kpm_subs.h" +#include "ran_e2sm_ue_id.h" -#if defined (NGRAN_GNB_DU) -#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h" -#include "openair2/LAYER2/nr_rlc/nr_rlc_entity.h" -#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" -#endif - -#if defined (NGRAN_GNB_CUCP) -#include "openair2/RRC/NR/rrc_gNB_UE_context.h" -#include "openair3/NGAP/ngap_gNB_ue_context.h" -#endif - -#if defined (NGRAN_GNB_CUUP) -#include "openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h" -#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h" #include "openair2/E1AP/e1ap_common.h" +#include "openair2/E2AP/flexric/src/util/time_now_us.h" +#include "openair2/F1AP/f1ap_ids.h" + +static pthread_once_t once_kpm_mutex = PTHREAD_ONCE_INIT; + +static void init_once_kpm(void) +{ + init_kpm_subs_data(); +} + +static ngran_node_t get_e2_node_type(void) +{ + ngran_node_t node_type = 0; + +#if defined(NGRAN_GNB_DU) && defined(NGRAN_GNB_CUUP) && defined(NGRAN_GNB_CUCP) + node_type = RC.nrrrc[0]->node_type; +#elif defined (NGRAN_GNB_CUUP) + node_type = ngran_gNB_CUUP; #endif -#include "openair2/F1AP/f1ap_ids.h" -#include -#include + return node_type; +} -/* Please note that only one executable nr-softmodem exists for DU, CU, CU-CP and gNB-mono nodes; - the layer separation should be done better in the future */ +typedef struct { + size_t sz; + ue_id_e2sm_t* ue_id; -#if defined (NGRAN_GNB_CUUP) -typedef struct uldlcounter { - uint32_t dl; - uint32_t ul; -} uldlcounter_t; + // Optional + // only used to retreive MAC/RLC stats + NR_UE_info_t* ue_info_list; +}arr_ue_id_t; + +static meas_data_lst_t fill_kpm_meas_data_item(const meas_info_format_1_lst_t* meas_info_lst, const size_t len, const uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +{ + meas_data_lst_t data_item = {0}; + + // Measurement Record + data_item.meas_record_len = len; // record data list length corresponds to info list length from action definition + + data_item.meas_record_lst = calloc(len, sizeof(meas_record_lst_t)); + assert(data_item.meas_record_lst != NULL && "Memory exhausted"); + + for (size_t i = 0; i < len; i++) { + // Measurement Type as requested in Action Definition + assert(meas_info_lst[i].meas_type.type == NAME_MEAS_TYPE && "Only NAME supported"); + + char* meas_info_name_str = cp_ba_to_str(meas_info_lst[i].meas_type.name); + + data_item.meas_record_lst[i] = get_kpm_meas_value(meas_info_name_str, gran_period_ms, ue_info, ue_idx); + + free(meas_info_name_str); + } + + return data_item; +} + +static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1(cudu_ue_info_pair_t ue_info, const size_t ue_idx, const kpm_act_def_format_1_t* act_def_fr_1) +{ + kpm_ind_msg_format_1_t msg_frm_1 = {0}; + + // Measurement Data contains a set of Meas Records, each collected at each granularity period + msg_frm_1.meas_data_lst_len = 1; /* this value is equal to (kpm_ric_event_trigger_format_1.report_period_ms/act_def_fr_1->gran_period_ms) + please, check their values in xApp */ + + msg_frm_1.meas_data_lst = calloc(msg_frm_1.meas_data_lst_len, sizeof(*msg_frm_1.meas_data_lst)); + assert(msg_frm_1.meas_data_lst != NULL && "Memory exhausted" ); + + msg_frm_1.meas_data_lst[0] = fill_kpm_meas_data_item(act_def_fr_1->meas_info_lst, + act_def_fr_1->meas_info_lst_len, + act_def_fr_1->gran_period_ms, + ue_info, + ue_idx); + + // Measurement Information - OPTIONAL + msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len; + msg_frm_1.meas_info_lst = calloc(msg_frm_1.meas_info_lst_len, sizeof(meas_info_format_1_lst_t)); + assert(msg_frm_1.meas_info_lst != NULL && "Memory exhausted"); + for (size_t i = 0; i < msg_frm_1.meas_info_lst_len; i++) { + msg_frm_1.meas_info_lst[i] = cp_meas_info_format_1_lst(&act_def_fr_1->meas_info_lst[i]); + } + + return msg_frm_1; +} + +static cudu_ue_info_pair_t fill_ue_related_info(arr_ue_id_t* arr_ue_id, const size_t ue_idx) +{ + cudu_ue_info_pair_t ue_info = {0}; + + if (arr_ue_id->ue_id[ue_idx].type == GNB_UE_ID_E2SM) { + ue_info.rrc_ue_id = *arr_ue_id->ue_id[ue_idx].gnb.ran_ue_id; // rrc_ue_id + ue_info.ue = &arr_ue_id->ue_info_list[ue_idx]; + } else if (arr_ue_id->ue_id[ue_idx].type == GNB_CU_UP_UE_ID_E2SM) { + /* in OAI implementation, CU-UP ue id = CU-CP ue id + => CU-UP ue id = rrc_ue_id, but it should not be the case by the spec */ + ue_info.rrc_ue_id = *arr_ue_id->ue_id[ue_idx].gnb_cu_up.ran_ue_id; // cucp_ue_id = rrc_ue_id + } else if (arr_ue_id->ue_id[ue_idx].type == GNB_DU_UE_ID_E2SM) { + ue_info.rrc_ue_id = *arr_ue_id->ue_id[ue_idx].gnb_du.ran_ue_id; // rrc_ue_id + ue_info.ue = &arr_ue_id->ue_info_list[ue_idx]; + } + + return ue_info; +} + +static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3(arr_ue_id_t* arr_ue_id, const kpm_act_def_format_1_t* act_def_fr_1) +{ + assert(act_def_fr_1 != NULL); + + kpm_ind_msg_format_3_t msg_frm_3 = {0}; + + // Fill UE Measurement Reports + msg_frm_3.ue_meas_report_lst_len = arr_ue_id->sz; + msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t)); + assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted"); + + for (size_t i = 0; iue_id[i]); + + // Fill UE related info + cudu_ue_info_pair_t ue_info = fill_ue_related_info(arr_ue_id, i); + + // Fill UE measurements + msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = fill_kpm_ind_msg_frm_1(ue_info, i, act_def_fr_1); + } + + return msg_frm_3; +} + +static void capture_sst_sd(test_cond_value_t* test_cond_value, uint8_t *sst, uint32_t **sd) +{ + DevAssert(sst != NULL); + DevAssert(sd != NULL); + + // S-NSSAI is an OCTET_STRING, as defined by spec + switch (test_cond_value->type) { + case OCTET_STRING_TEST_COND_VALUE: { + if (test_cond_value->octet_string_value->len == 1) { + *sst = test_cond_value->octet_string_value->buf[0]; + *sd = NULL; + } else { + DevAssert(test_cond_value->octet_string_value->len == 4); + uint8_t *buf = test_cond_value->octet_string_value->buf; + *sst = buf[0]; + *sd = malloc(**sd); + **sd = buf[1] << 16 | buf[2] << 8 | buf[3]; + } + break; + } + default: + AssertFatal(false, "test condition value %d impossible\n", test_cond_value->type); + } +} static bool nssai_matches(nssai_t a_nssai, uint8_t b_sst, const uint32_t *b_sd) { @@ -64,552 +187,162 @@ static bool nssai_matches(nssai_t a_nssai, uint8_t b_sst, const uint32_t *b_sd) } } -static uldlcounter_t last_pdcp_sdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; - -static gnb_cu_up_e2sm_t fill_gnb_cu_up_data(const ue_id_t cucp_ue_id) +static arr_ue_id_t filter_ues_by_s_nssai_in_cu(const test_info_lst_t test_info) { - gnb_cu_up_e2sm_t gnb_cu_up = {0}; + assert(test_info.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true"); + assert(test_info.test_cond != NULL && "Must be filled, even though it's optional by the specification"); + assert(*test_info.test_cond == EQUAL_TEST_COND && "Currently support EQUAL test condition"); + assert(test_info.test_cond_value != NULL && "Must be filled, even though it's optional by the specification"); + + uint8_t sst = 0; + uint32_t *sd = NULL; + capture_sst_sd(test_info.test_cond_value, &sst, &sd); - // 6.2.3.20 - // Mandatory - gnb_cu_up.gnb_cu_cp_ue_e1ap = cucp_ue_id; + arr_ue_id_t arr_ue_id = {.sz = 0}; + arr_ue_id.ue_id = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_e2sm_t)); + assert(arr_ue_id.ue_id != NULL); - // 6.2.3.25 - // RAN UE ID - // Optional - gnb_cu_up.ran_ue_id = NULL; + struct rrc_gNB_ue_context_s* rrc_ue_context = NULL; + RB_FOREACH(rrc_ue_context, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) { + gNB_RRC_UE_t *ue = &rrc_ue_context->ue_context; + for (int p = 0; p < ue->nb_of_pdusessions; ++p) { + pdusession_t *pdu = &ue->pduSession[p].param; + if (nssai_matches(pdu->nssai, sst, sd)) { + arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB_CU](ue, 0, 0); - return gnb_cu_up; + arr_ue_id.sz++; + break; + } + } + AssertFatal(arr_ue_id.sz < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n"); + } + + free(sd); // if NULL, nothing happens + + return arr_ue_id; } -typedef struct { - ue_id_t pdcp_ue_id_list[MAX_MOBILES_PER_GNB]; // list of matched UEs on PDCP-U level - size_t num_ues; - -} matched_ues_pdcp_t; - - __attribute__((unused)) -static size_t filter_ues_by_s_nssai_in_cuup(test_cond_e const condition, uint8_t sst, const uint32_t *sd, matched_ues_pdcp_t* matches) +static arr_ue_id_t filter_ues_by_s_nssai_in_cuup(const test_info_lst_t test_info) { - DevAssert(matches != NULL); - AssertFatal(condition == EQUAL_TEST_COND, "Condition %d not yet implemented\n", condition); + assert(test_info.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true"); + assert(test_info.test_cond != NULL && "Must be filled, even though it's optional by the specification"); + assert(*test_info.test_cond == EQUAL_TEST_COND && "Currently support EQUAL test condition"); + assert(test_info.test_cond_value != NULL && "Must be filled, even though it's optional by the specification"); + + uint8_t sst = 0; + uint32_t *sd = NULL; + capture_sst_sd(test_info.test_cond_value, &sst, &sd); + + arr_ue_id_t arr_ue_id = {.sz = 0}; + arr_ue_id.ue_id = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_e2sm_t)); + assert(arr_ue_id.ue_id != NULL); // Get NSSAI info from E1 context - const instance_t CUuniqInstance = 0; - const e1ap_upcp_inst_t *e1inst = getCxtE1(CUuniqInstance); + const instance_t e1_inst = 0; + const e1ap_upcp_inst_t *e1inst = getCxtE1(e1_inst); // currently the CU-UP does not store the slices, so we can only do this "coarse filtering" for(int s = 0; s < e1inst->cuup.setupReq.plmn[0].supported_slices; s++) { if (nssai_matches(e1inst->cuup.setupReq.plmn[0].slice[s], sst, sd)) { - matches->num_ues = nr_pdcp_get_num_ues(matches->pdcp_ue_id_list, MAX_MOBILES_PER_GNB); - } - } - - return matches->num_ues; -} + ue_id_t *pdcp_ue_id_list = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_t)); + assert(pdcp_ue_id_list != NULL); -static nr_pdcp_statistics_t get_pdcp_stats_per_drb(const uint32_t rrc_ue_id) -{ - nr_pdcp_statistics_t pdcp = {0}; - const int srb_flag = 0; - const int rb_id = 1; // at the moment, only 1 DRB is supported - - // Get PDCP stats for specific DRB - const bool rc = nr_pdcp_get_statistics(rrc_ue_id, srb_flag, rb_id, &pdcp); - assert(rc == true && "Cannot get PDCP stats\n"); - - return pdcp; -} - -static meas_record_lst_t fill_ue_pdcp_data(const uint32_t rrc_ue_id, const size_t ue_idx, const char * meas_info_name_str) -{ - meas_record_lst_t meas_record = {0}; - - // Get PDCP stats per DRB - nr_pdcp_statistics_t pdcp = get_pdcp_stats_per_drb(rrc_ue_id); - - // Measurement Type as requested in Action Definition - if (strcmp(meas_info_name_str, "DRB.PdcpSduVolumeDL") == 0) { // 3GPP TS 28.522 - section 5.1.2.1.1.1 - meas_record.value = INTEGER_MEAS_VALUE; - - // Get DL data volume delivered to PDCP layer - meas_record.int_val = (pdcp.rxsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].dl)*8/1000; // [kb] - last_pdcp_sdu_total_bytes[ue_idx].dl = pdcp.rxsdu_bytes; - /* note: this measurement is calculated as per spec */ - } else if (strcmp(meas_info_name_str, "DRB.PdcpSduVolumeUL") == 0) { // 3GPP TS 28.522 - section 5.1.2.1.2.1 - meas_record.value = INTEGER_MEAS_VALUE; - - // Get UL data volume delivered from PDCP layer - meas_record.int_val = (pdcp.txsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].ul)*8/1000; // [kb] - last_pdcp_sdu_total_bytes[ue_idx].ul = pdcp.txsdu_bytes; - /* note: this measurement is calculated as per spec */ - } else { - assert(false && "Measurement Name not yet implemented"); - } - - - return meas_record; -} - -static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_cu(const uint32_t rrc_ue_id, const size_t ue_idx, const kpm_act_def_format_1_t * act_def_fr_1) -{ - kpm_ind_msg_format_1_t msg_frm_1 = {0}; - - // Measurement Data contains a set of Meas Records, each collected at each granularity period - msg_frm_1.meas_data_lst_len = 1; /* this value is equal to (kpm_ric_event_trigger_format_1.report_period_ms/act_def_fr_1->gran_period_ms) - please, check their values in xApp */ - - msg_frm_1.meas_data_lst = calloc(msg_frm_1.meas_data_lst_len, sizeof(*msg_frm_1.meas_data_lst)); - assert(msg_frm_1.meas_data_lst != NULL && "Memory exhausted" ); - - for (size_t i = 0; imeas_info_lst_len; // record data list length corresponds to info list length from action definition - - msg_frm_1.meas_data_lst[i].meas_record_lst = calloc(msg_frm_1.meas_data_lst[i].meas_record_len, sizeof(meas_record_lst_t)); - assert(msg_frm_1.meas_data_lst[i].meas_record_lst != NULL && "Memory exhausted"); - - for (size_t j = 0; j < msg_frm_1.meas_data_lst[i].meas_record_len; j++) { // each meas record corresponds to one meas type - // Measurement Type as requested in Action Definition - switch (act_def_fr_1->meas_info_lst[j].meas_type.type) { - case NAME_MEAS_TYPE: { - char meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len + 1]; - memcpy(meas_info_name_str, act_def_fr_1->meas_info_lst[j].meas_type.name.buf, act_def_fr_1->meas_info_lst[j].meas_type.name.len); - meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len] = '\0'; - - msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_pdcp_data(rrc_ue_id, ue_idx, meas_info_name_str); - break; - } - case ID_MEAS_TYPE: - assert(false && "ID Measurement Type not yet implemented"); - - default: - assert(false && "Measurement Type not recognized"); + arr_ue_id.sz = nr_pdcp_get_num_ues(pdcp_ue_id_list, MAX_MOBILES_PER_GNB); + for (size_t i = 0; i < arr_ue_id.sz; i++) { + arr_ue_id.ue_id[i] = fill_ue_id_data[ngran_gNB_CUUP](NULL, 0, pdcp_ue_id_list[i]); } + free(pdcp_ue_id_list); } } - // Measurement Information - OPTIONAL - msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len; - msg_frm_1.meas_info_lst = calloc(msg_frm_1.meas_info_lst_len, sizeof(meas_info_format_1_lst_t)); - assert(msg_frm_1.meas_info_lst != NULL && "Memory exhausted"); - for (size_t i = 0; i < msg_frm_1.meas_info_lst_len; i++) { - msg_frm_1.meas_info_lst[i] = cp_meas_info_format_1_lst(&act_def_fr_1->meas_info_lst[i]); - } + free(sd); // if NULL, nothing happens - return msg_frm_1; + return arr_ue_id; } - __attribute__((unused)) -static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_cuup(const matched_ues_pdcp_t* matched_ues, - const kpm_act_def_format_1_t* act_def_fr_1) +static arr_ue_id_t filter_ues_by_s_nssai_in_du_or_monolithic(const test_info_lst_t test_info) { - assert(act_def_fr_1 != NULL); - - kpm_ind_msg_format_3_t msg_frm_3 = {0}; - - // Fill UE Measurement Reports - - msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues; - msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t)); - assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted"); - - for (size_t i = 0; ipdcp_ue_id_list[i]); - - // Fill UE related info - msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = - fill_kpm_ind_msg_frm_1_in_cu(matched_ues->pdcp_ue_id_list[i], i, act_def_fr_1); - } - - return msg_frm_3; -} -#endif - -#if defined (NGRAN_GNB_DU) -static uldlcounter_t last_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; -static uldlcounter_t last_total_prbs[MAX_MOBILES_PER_GNB] = {0}; - -static gnb_e2sm_t fill_gnb_data(rrc_gNB_ue_context_t * ue_context_p) -{ - gnb_e2sm_t gnb = {0}; - - // 6.2.3.16 - // Mandatory - // AMF UE NGAP ID - gnb.amf_ue_ngap_id = ue_context_p->ue_context.rrc_ue_id; - - // Mandatory - //GUAMI 6.2.3.17 - gnb.guami.plmn_id = (e2sm_plmn_t) { - .mcc = ue_context_p->ue_context.ue_guami.mcc, - .mnc = ue_context_p->ue_context.ue_guami.mnc, - .mnc_digit_len = ue_context_p->ue_context.ue_guami.mnc_len - }; + assert(test_info.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true"); + assert(test_info.test_cond != NULL && "Must be filled, even though it's optional by the specification"); + assert(*test_info.test_cond == EQUAL_TEST_COND && "Currently support EQUAL test condition"); + assert(test_info.test_cond_value != NULL && "Must be filled, even though it's optional by the specification"); - gnb.guami.amf_region_id = ue_context_p->ue_context.ue_guami.amf_region_id; - gnb.guami.amf_set_id = ue_context_p->ue_context.ue_guami.amf_set_id; - gnb.guami.amf_ptr = ue_context_p->ue_context.ue_guami.amf_pointer; + uint8_t sst = 0; + uint32_t *sd = NULL; + capture_sst_sd(test_info.test_cond_value, &sst, &sd); - // gNB-CU UE F1AP ID List - // C-ifCUDUseparated - if (NODE_IS_CU(RC.nrrrc[0]->node_type)) { - gnb.gnb_cu_ue_f1ap_lst_len = 1; - gnb.gnb_cu_ue_f1ap_lst = calloc(gnb.gnb_cu_ue_f1ap_lst_len, sizeof(uint32_t)); - assert(gnb.gnb_cu_ue_f1ap_lst != NULL); + arr_ue_id_t arr_ue_id = {.sz = 0}; + arr_ue_id.ue_id = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_e2sm_t)); + assert(arr_ue_id.ue_id != NULL); - for (size_t i = 0; i < gnb.gnb_cu_ue_f1ap_lst_len; i++) { - gnb.gnb_cu_ue_f1ap_lst[i] = ue_context_p->ue_context.rrc_ue_id; - } - } + arr_ue_id.ue_info_list = calloc(MAX_MOBILES_PER_GNB, sizeof(NR_UE_info_t)); + assert(arr_ue_id.ue_info_list != NULL); - return gnb; -} - -static gnb_du_e2sm_t fill_gnb_du_data(const f1_ue_data_t * rrc_ue_id) -{ - gnb_du_e2sm_t gnb_du = {0}; - - // 6.2.3.21 - // gNB CU UE F1AP - // Mandatory - gnb_du.gnb_cu_ue_f1ap = rrc_ue_id->secondary_ue; - - // 6.2.3.25 - // RAN UE ID - // Optional - gnb_du.ran_ue_id = NULL; - - return gnb_du; -} -typedef struct { - NR_UE_info_t* ue_list[MAX_MOBILES_PER_GNB]; - size_t num_ues; -} matched_ues_mac_t; - -static size_t filter_ues_by_s_nssai_in_du_or_monolithic(test_cond_e const condition, - uint8_t sst, - const uint32_t *sd, - matched_ues_mac_t* matches) -{ - DevAssert(matches != NULL); - AssertFatal(condition == EQUAL_TEST_COND, "Condition %d not yet implemented", condition); + const ngran_node_t node_type = get_e2_node_type(); // Take MAC info - size_t i = 0; UE_iterator (RC.nrmac[0]->UE_info.list, ue) { NR_UE_sched_ctrl_t *sched_ctrl = &ue->UE_sched_ctrl; // UE matches if any of its DRBs matches for (int l = 0; l < sched_ctrl->dl_lc_num; ++l) { long lcid = sched_ctrl->dl_lc_ids[l]; if (nssai_matches(sched_ctrl->dl_lc_nssai[lcid], sst, sd)) { - matches->ue_list[i++] = ue; - break; - } - } - AssertFatal(i < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n"); - } - - matches->num_ues = i; - - return i; -} - -static nr_rlc_statistics_t get_rlc_stats_per_drb(NR_UE_info_t const * UE) -{ - assert(UE != NULL); - - nr_rlc_statistics_t rlc = {0}; - const int srb_flag = 0; - const int rb_id = 1; // at the moment, only 1 DRB is supported - - // Get RLC stats for specific DRB - const bool rc = nr_rlc_get_statistics(UE->rnti, srb_flag, rb_id, &rlc); - assert(rc == true && "Cannot get RLC stats\n"); - - // Activate average sojourn time at the RLC buffer for specific DRB - nr_rlc_activate_avg_time_to_tx(UE->rnti, rb_id+3, 1); - - return rlc; -} - -static meas_record_lst_t fill_ue_mac_rlc_data(const NR_UE_info_t* UE, const size_t ue_idx, const char * meas_info_name_str, const uint32_t gran_period_ms) -{ - assert(UE != NULL); - - meas_record_lst_t meas_record = {0}; - - // Get RLC stats per DRB - nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(UE); - - // Measurement Type as requested in Action Definition - if (strcmp(meas_info_name_str, "DRB.UEThpDl") == 0) { // 3GPP TS 28.522 - section 5.1.1.3.1 - meas_record.value = REAL_MEAS_VALUE; - - // Calculate DL Thp - meas_record.real_val = (double)(rlc.txpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].dl)*8/gran_period_ms; // [kbps] - last_rlc_pdu_total_bytes[ue_idx].dl = rlc.txpdu_bytes; - /* note: per spec, average UE throughput in DL (taken into consideration values from all UEs, and averaged) - here calculated as: UE specific throughput in DL */ - } else if (strcmp(meas_info_name_str, "DRB.UEThpUl") == 0) { // 3GPP TS 28.522 - section 5.1.1.3.3 - meas_record.value = REAL_MEAS_VALUE; - - // Calculate UL Thp - meas_record.real_val = (double)(rlc.rxpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].ul)*8/gran_period_ms; // [kbps] - last_rlc_pdu_total_bytes[ue_idx].ul = rlc.rxpdu_bytes; - /* note: per spec, average UE throughput in UL (taken into consideration values from all UEs, and averaged) - here calculated as: UE specific throughput in UL */ - } else if (strcmp(meas_info_name_str, "DRB.RlcSduDelayDl") == 0) { // 3GPP TS 28.522 - section 5.1.3.3.3 - meas_record.value = REAL_MEAS_VALUE; - - // Get the value of sojourn time at the RLC buffer - meas_record.real_val = rlc.txsdu_avg_time_to_tx; // [μs] - /* note: by default this measurement is calculated for previous 100ms (openair2/LAYER2/nr_rlc/nr_rlc_entity.c:118, 173, 213); please, update according to your needs */ - } else if (strcmp(meas_info_name_str, "RRU.PrbTotDl") == 0) { // 3GPP TS 28.522 - section 5.1.1.2.1 - meas_record.value = INTEGER_MEAS_VALUE; - - // Get the number of DL PRBs - meas_record.int_val = UE->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl; // [PRBs] - last_total_prbs[ue_idx].dl = UE->mac_stats.dl.total_rbs; - /* note: per spec, DL PRB usage [%] = (total used PRBs for DL traffic / total available PRBs for DL traffic) * 100 - here calculated as: aggregated DL PRBs (t) - aggregated DL PRBs (t-gran_period) */ - } else if (strcmp(meas_info_name_str, "RRU.PrbTotUl") == 0) { // 3GPP TS 28.522 - section 5.1.1.2.2 - meas_record.value = INTEGER_MEAS_VALUE; - - // Get the number of UL PRBs - meas_record.int_val = UE->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul; // [PRBs] - last_total_prbs[ue_idx].ul = UE->mac_stats.ul.total_rbs; - /* note: per spec, UL PRB usage [%] = (total used PRBs for UL traffic / total available PRBs for UL traffic) * 100 - here calculated as: aggregated UL PRBs (t) - aggregated UL PRBs (t-gran_period) */ - } else { - assert(false && "Measurement Name not yet implemented"); - } - - return meas_record; -} - -static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_du(const NR_UE_info_t* UE, const size_t ue_idx, const kpm_act_def_format_1_t * act_def_fr_1) -{ - kpm_ind_msg_format_1_t msg_frm_1 = {0}; - - // Measurement Data contains a set of Meas Records, each collected at each granularity period - msg_frm_1.meas_data_lst_len = 1; /* this value is equal to (kpm_ric_event_trigger_format_1.report_period_ms/act_def_fr_1->gran_period_ms) - please, check their values in xApp */ - - msg_frm_1.meas_data_lst = calloc(msg_frm_1.meas_data_lst_len, sizeof(*msg_frm_1.meas_data_lst)); - assert(msg_frm_1.meas_data_lst != NULL && "Memory exhausted" ); - - for (size_t i = 0; imeas_info_lst_len; // record data list length corresponds to info list length from action definition - - msg_frm_1.meas_data_lst[i].meas_record_lst = calloc(msg_frm_1.meas_data_lst[i].meas_record_len, sizeof(meas_record_lst_t)); - assert(msg_frm_1.meas_data_lst[i].meas_record_lst != NULL && "Memory exhausted"); - - for (size_t j = 0; j < msg_frm_1.meas_data_lst[i].meas_record_len; j++) { // each meas record corresponds to one meas type - // Measurement Type as requested in Action Definition - switch (act_def_fr_1->meas_info_lst[j].meas_type.type) { - case NAME_MEAS_TYPE: { - char meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len + 1]; - memcpy(meas_info_name_str, act_def_fr_1->meas_info_lst[j].meas_type.name.buf, act_def_fr_1->meas_info_lst[j].meas_type.name.len); - meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len] = '\0'; - - msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_mac_rlc_data(UE, ue_idx, meas_info_name_str, act_def_fr_1->gran_period_ms); - break; - } - case ID_MEAS_TYPE: - assert(false && "ID Measurement Type not yet implemented"); - - default: - assert(false && "Measurement Type not recognized"); - } - } - } - - // Measurement Information - OPTIONAL - msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len; - msg_frm_1.meas_info_lst = calloc(msg_frm_1.meas_info_lst_len, sizeof(meas_info_format_1_lst_t)); - assert(msg_frm_1.meas_info_lst != NULL && "Memory exhausted"); - for (size_t i = 0; i < msg_frm_1.meas_info_lst_len; i++) { - msg_frm_1.meas_info_lst[i] = cp_meas_info_format_1_lst(&act_def_fr_1->meas_info_lst[i]); - } - - return msg_frm_1; -} - -static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_du(const matched_ues_mac_t* matched_ues, - const kpm_act_def_format_1_t* act_def_fr_1) -{ - assert(act_def_fr_1 != NULL); - - - kpm_ind_msg_format_3_t msg_frm_3 = {0}; - - // Fill UE Measurement Reports - - msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues; - msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t)); - assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted"); - - for (size_t i = 0; iue_list[i]->rnti); // get gNB CU UE ID as rrc_ue_id - msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_DU_UE_ID_E2SM; - msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb_du = fill_gnb_du_data(&rrc_ue_id); - - // Fill UE related info - msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = fill_kpm_ind_msg_frm_1_in_du(matched_ues->ue_list[i], i, act_def_fr_1); - } - - - return msg_frm_3; -} - -static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_monolithic(const NR_UE_info_t* UE, const size_t ue_idx, const uint32_t rrc_ue_id, const kpm_act_def_format_1_t * act_def_fr_1) -{ - kpm_ind_msg_format_1_t msg_frm_1 = {0}; - - // Measurement Data contains a set of Meas Records, each collected at each granularity period - msg_frm_1.meas_data_lst_len = 1; /* this value is equal to (kpm_ric_event_trigger_format_1.report_period_ms/act_def_fr_1->gran_period_ms) - please, check their values in xApp */ - - msg_frm_1.meas_data_lst = calloc(msg_frm_1.meas_data_lst_len, sizeof(*msg_frm_1.meas_data_lst)); - assert(msg_frm_1.meas_data_lst != NULL && "Memory exhausted" ); - - for (size_t i = 0; imeas_info_lst_len; // record data list length corresponds to info list length from action definition - - msg_frm_1.meas_data_lst[i].meas_record_lst = calloc(msg_frm_1.meas_data_lst[i].meas_record_len, sizeof(meas_record_lst_t)); - assert(msg_frm_1.meas_data_lst[i].meas_record_lst != NULL && "Memory exhausted"); - - for (size_t j = 0; j < msg_frm_1.meas_data_lst[i].meas_record_len; j++) { // each meas record corresponds to one meas type - // Measurement Type as requested in Action Definition - switch (act_def_fr_1->meas_info_lst[j].meas_type.type) { - case NAME_MEAS_TYPE: { - char meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len + 1]; - memcpy(meas_info_name_str, act_def_fr_1->meas_info_lst[j].meas_type.name.buf, act_def_fr_1->meas_info_lst[j].meas_type.name.len); - meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len] = '\0'; - - if (strcmp(meas_info_name_str, "DRB.PdcpSduVolumeDL") == 0 || strcmp(meas_info_name_str, "DRB.PdcpSduVolumeUL") == 0) { - msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_pdcp_data(rrc_ue_id, ue_idx, meas_info_name_str); + if (node_type == ngran_gNB_DU) { + f1_ue_data_t rrc_ue_id = du_get_f1_ue_data(ue->rnti); // gNB CU UE ID = rrc_ue_id + arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB_DU](NULL, rrc_ue_id.secondary_ue, 0); } else { - msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_mac_rlc_data(UE, ue_idx, meas_info_name_str, act_def_fr_1->gran_period_ms); + rrc_gNB_ue_context_t* rrc_ue_context = rrc_gNB_get_ue_context_by_rnti(RC.nrrrc[0], -1, ue->rnti); + arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB](&rrc_ue_context->ue_context, 0, 0); } - - break; - } - case ID_MEAS_TYPE: - assert(false && "ID Measurement Type not yet implemented"); - default: - assert(false && "Measurement Type not recognized"); - } - } - } + // store NR_UE_info_t + arr_ue_id.ue_info_list[arr_ue_id.sz] = *ue; - // Measurement Information - OPTIONAL - msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len; - msg_frm_1.meas_info_lst = calloc(msg_frm_1.meas_info_lst_len, sizeof(meas_info_format_1_lst_t)); - assert(msg_frm_1.meas_info_lst != NULL && "Memory exhausted"); - for (size_t i = 0; i < msg_frm_1.meas_info_lst_len; i++) { - msg_frm_1.meas_info_lst[i] = cp_meas_info_format_1_lst(&act_def_fr_1->meas_info_lst[i]); - } - - return msg_frm_1; -} - -static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_monolithic(const matched_ues_mac_t* matched_ues, - const kpm_act_def_format_1_t* act_def_fr_1) -{ - assert(act_def_fr_1 != NULL); - - - kpm_ind_msg_format_3_t msg_frm_3 = {0}; - - // Fill UE Measurement Reports - - msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues; - msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t)); - assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted"); - - - for (size_t i = 0; iue_list[i]->rnti); - msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_UE_ID_E2SM; - msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb = fill_gnb_data(rrc_ue_context_list); - - // Fill UE related info - msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = - fill_kpm_ind_msg_frm_1_in_monolithic(matched_ues->ue_list[i], i, rrc_ue_context_list->ue_context.rrc_ue_id, act_def_fr_1); - } - - return msg_frm_3; -} -#endif - -#if defined (NGRAN_GNB_CUCP) -typedef struct { - uint32_t rrc_ue_id_list[MAX_MOBILES_PER_GNB]; // list of matched UEs on RRC level containing only rrc_ue_id (gNB CU UE ID) - size_t num_ues; - -} matched_ues_rrc_t; - -static size_t filter_ues_by_s_nssai_in_cu(test_cond_e const condition, uint8_t sst, const uint32_t *sd, matched_ues_rrc_t* matches) -{ - DevAssert(matches != NULL); - AssertFatal(condition == EQUAL_TEST_COND, "Condition %d not yet implemented\n", condition); - - struct rrc_gNB_ue_context_s *ue_context_p1 = NULL; - size_t i = 0; - RB_FOREACH(ue_context_p1, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) { - gNB_RRC_UE_t *ue = &ue_context_p1->ue_context; - for (int p = 0; p < ue->nb_of_pdusessions; ++p) { - pdusession_t *pdu = &ue->pduSession[0].param; - if (nssai_matches(pdu->nssai, sst, sd)) { - matches->rrc_ue_id_list[i++] = ue_context_p1->ue_context.rrc_ue_id; + arr_ue_id.sz++; break; } } - AssertFatal(i < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n"); + AssertFatal(arr_ue_id.sz < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n"); } - matches->num_ues = i; - - return i; + free(sd); // if NULL, nothing happens + + return arr_ue_id; } -static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_cu(const matched_ues_rrc_t* matched_ues, - const kpm_act_def_format_1_t* act_def_fr_1) +typedef arr_ue_id_t (*ue_type_matcher)(const test_info_lst_t test_info); + +static ue_type_matcher match_s_nssai_test_cond_type[END_NGRAN_NODE_TYPE] = { - assert(act_def_fr_1 != NULL); + NULL, + NULL, + filter_ues_by_s_nssai_in_du_or_monolithic, + NULL, + NULL, + filter_ues_by_s_nssai_in_cu, + NULL, + filter_ues_by_s_nssai_in_du_or_monolithic, + NULL, + NULL, // for CU-CP we don't use this + filter_ues_by_s_nssai_in_cuup, +}; +typedef ue_type_matcher (*filter_ues)[END_NGRAN_NODE_TYPE]; - kpm_ind_msg_format_3_t msg_frm_3 = {0}; +static filter_ues match_cond_arr[END_TEST_COND_TYPE_KPM_V2_01] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &match_s_nssai_test_cond_type, +}; - // Fill UE Measurement Reports - - msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues; - msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t)); - assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted"); - - for (size_t i = 0; irrc_ue_id_list[i]); - msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_UE_ID_E2SM; - msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb = fill_gnb_data(rrc_ue_context_list); - - // Fill UE related info - msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = - fill_kpm_ind_msg_frm_1_in_cu(matched_ues->rrc_ue_id_list[i], i, act_def_fr_1); - } - - return msg_frm_3; -} -#endif static kpm_ric_ind_hdr_format_1_t kpm_ind_hdr_frm_1(void) { @@ -627,60 +360,10 @@ static kpm_ric_ind_hdr_format_1_t kpm_ind_hdr_frm_1(void) hdr_frm_1.fileformat_version = NULL; // Check E2 Node NG-RAN Type - #if defined (NGRAN_GNB_DU) - if (RC.nrrrc[0]->node_type == ngran_gNB_DU) { - hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t)); - hdr_frm_1.sender_name->buf = calloc(strlen("OAI-DU") + 1, sizeof(char)); - memcpy(hdr_frm_1.sender_name->buf, "OAI-DU", strlen("OAI-DU")); - hdr_frm_1.sender_name->len = strlen("OAI-DU"); - - hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t)); - hdr_frm_1.sender_type->buf = calloc(strlen("DU") + 1, sizeof(char)); - memcpy(hdr_frm_1.sender_type->buf, "DU", strlen("DU")); - hdr_frm_1.sender_type->len = strlen("DU"); - } else if (RC.nrrrc[0]->node_type == ngran_gNB_CU) { - hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t)); - hdr_frm_1.sender_name->buf = calloc(strlen("OAI-CU") + 1, sizeof(char)); - memcpy(hdr_frm_1.sender_name->buf, "OAI-CU", strlen("OAI-CU")); - hdr_frm_1.sender_name->len = strlen("OAI-CU"); - - hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t)); - hdr_frm_1.sender_type->buf = calloc(strlen("CU") + 1, sizeof(char)); - memcpy(hdr_frm_1.sender_type->buf, "CU", strlen("CU")); - hdr_frm_1.sender_type->len = strlen("CU"); - } else if (RC.nrrrc[0]->node_type == ngran_gNB) { - hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t)); - hdr_frm_1.sender_name->buf = calloc(strlen("OAI-MONO") + 1, sizeof(char)); - memcpy(hdr_frm_1.sender_name->buf, "OAI-MONO", strlen("OAI-MONO")); - hdr_frm_1.sender_name->len = strlen("OAI-MONO"); - - hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t)); - hdr_frm_1.sender_type->buf = calloc(strlen("MONO") + 1, sizeof(char)); - memcpy(hdr_frm_1.sender_type->buf, "MONO", strlen("MONO")); - hdr_frm_1.sender_type->len = strlen("MONO"); - } else if (RC.nrrrc[0]->node_type == ngran_gNB_CUCP) { - return hdr_frm_1; // nothing to fill - } else { - assert(0!=0 && "Unknown node type"); - } - #endif - - #if defined (NGRAN_GNB_CUUP) + const ngran_node_t node_type = get_e2_node_type(); + const char* sender_name = get_ngran_name(node_type); hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t)); - hdr_frm_1.sender_name->buf = calloc(strlen("OAI-CU-UP") + 1, sizeof(char)); - memcpy(hdr_frm_1.sender_name->buf, "OAI-CU-UP", strlen("OAI-CU-UP")); - hdr_frm_1.sender_name->len = strlen("OAI-CU-UP"); - - hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t)); - hdr_frm_1.sender_type->buf = calloc(strlen("CU-UP") + 1, sizeof(char)); - memcpy(hdr_frm_1.sender_type->buf, "CU-UP", strlen("CU-UP")); - hdr_frm_1.sender_type->len = strlen("CU-UP"); - #endif - - hdr_frm_1.vendor_name = calloc(1, sizeof(byte_array_t)); - hdr_frm_1.vendor_name->buf = calloc(strlen("OAI") + 1, sizeof(char)); - memcpy(hdr_frm_1.vendor_name->buf, "OAI", strlen("OAI")); - hdr_frm_1.vendor_name->len = strlen("OAI"); + *hdr_frm_1.sender_name = cp_str_to_ba(sender_name); return hdr_frm_1; } @@ -695,40 +378,6 @@ kpm_ind_hdr_t kpm_ind_hdr(void) return hdr; } -static void capture_sst_sd(test_info_lst_t* test, uint8_t *sst, uint32_t **sd) -{ - DevAssert(test != NULL && test->test_cond_value != NULL); - DevAssert(sst != NULL); - DevAssert(sd != NULL); - - // we made a mistake in the past: NSSAI is supposed to be an OCTET_STRING, - // but earlier version of the RAN function and the xApp used integer, so - // handle this gracefully by accepting integer as well - switch (test->test_cond_value->type) { - case INTEGER_TEST_COND_VALUE: { - AssertFatal(*test->test_cond_value->int_value <= 0xff, "illegal SST %ld\n", *test->test_cond_value->int_value); - *sst = *test->test_cond_value->int_value; - *sd = NULL; - break; - } - case OCTET_STRING_TEST_COND_VALUE: { - if (test->test_cond_value->octet_string_value->len == 1) { - *sst = test->test_cond_value->octet_string_value->buf[0]; - *sd = NULL; - } else { - DevAssert(test->test_cond_value->octet_string_value->len == 4); - uint8_t *buf = test->test_cond_value->octet_string_value->buf; - *sst = buf[0]; - *sd = malloc(**sd); - **sd = buf[1] << 16 | buf[2] << 8 | buf[3]; - } - break; - } - default: - AssertFatal(false, "test condition value %d impossible\n", test->test_cond_value->type); - } -} - bool read_kpm_sm(void* data) { assert(data != NULL); @@ -748,113 +397,17 @@ bool read_kpm_sm(void* data) // Filter the UE by the test condition criteria kpm_act_def_format_4_t const* frm_4 = &kpm->act_def->frm_4; // 8.2.1.2.4 for (size_t i = 0; i < frm_4->matching_cond_lst_len; i++) { - switch (frm_4->matching_cond_lst[i].test_info_lst.test_cond_type) { - case GBR_TEST_COND_TYPE: { - assert(0 != 0 && "Not implemented"); - break; - } - case AMBR_TEST_COND_TYPE: { - assert(0 != 0 && "Not implemented"); - break; - } - case IsStat_TEST_COND_TYPE: { - assert(0 != 0 && "Not implemented"); - break; - } - case IsCatM_TEST_COND_TYPE: { - assert(0 != 0 && "Not implemented"); - break; - } + const test_info_lst_t test_info = frm_4->matching_cond_lst[i].test_info_lst; + const ngran_node_t node_type = get_e2_node_type(); - case DL_RSRP_TEST_COND_TYPE: { - assert(0 != 0 && "Not implemented"); - break; - } + /* Based on the node type (gNB-mono, CU, DU,...) and matching condition (S-NSSAI, DL RSRP, GBR,...) + get array of E2SM UE IDs with optionally their MAC info */ + ue_type_matcher fp_match_cond_type = (*match_cond_arr[test_info.test_cond_type])[node_type]; + arr_ue_id_t arr_ue_id = fp_match_cond_type(test_info); - case DL_RSRQ_TEST_COND_TYPE: { - assert(0 != 0 && "Not implemented"); - break; - } - - case UL_RSRP_TEST_COND_TYPE: { - assert(0 != 0 && "Not implemented"); - break; - } - - case CQI_TEST_COND_TYPE: { - assert(0 != 0 && "Not implemented"); - break; - } - - case fiveQI_TEST_COND_TYPE: { - assert(0 != 0 && "Not implemented"); - break; - } - - case QCI_TEST_COND_TYPE: { - ; - assert(0 != 0 && "Not implemented"); - break; - } - case S_NSSAI_TEST_COND_TYPE: { - assert(frm_4->matching_cond_lst[i].test_info_lst.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true"); - assert(frm_4->matching_cond_lst[i].test_info_lst.test_cond != NULL && "Even though is optional.."); - assert(frm_4->matching_cond_lst[i].test_info_lst.test_cond_value != NULL && "Even though is optional.."); - - test_cond_e const test_cond = *frm_4->matching_cond_lst[i].test_info_lst.test_cond; - uint8_t sst = 0; - uint32_t *sd = NULL; - capture_sst_sd(&frm_4->matching_cond_lst[i].test_info_lst, &sst, &sd); - // Check E2 Node NG-RAN Type - /* Current implementation: - - we only have one executable nr-softmodem for 4 node types: DU, CU, gNB-mono, CU-CP - In the future, when nr-softmodem is separated, linking should be done as following: - DU: e2_ran_func_du - CU-CP : e2_ran_func_cucp - CU-UP: e2_ran_func_cuup (this is available at the moment, executable nr-cuup) - CU: e2_ran_func_cucp + e2_ran_func_cuup - gNB-mono: e2_ran_func_du + e2_ran_func_cucp + e2_ran_func_cuup */ - #if defined (NGRAN_GNB_DU) - if (RC.nrrrc[0]->node_type == ngran_gNB_DU) { - matched_ues_mac_t matched_ues = {0}; - matched_ues.num_ues = filter_ues_by_s_nssai_in_du_or_monolithic(test_cond, sst, sd, &matched_ues); - if (matched_ues.num_ues == 0) return false; - kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_du(&matched_ues, &frm_4->action_def_format_1); - } else if (RC.nrrrc[0]->node_type == ngran_gNB_CU) { - matched_ues_rrc_t matched_ues = {0}; - matched_ues.num_ues = filter_ues_by_s_nssai_in_cu(test_cond, sst, sd, &matched_ues); - if (matched_ues.num_ues == 0) return false; - kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_cu(&matched_ues, &frm_4->action_def_format_1); - } else if (RC.nrrrc[0]->node_type == ngran_gNB) { - matched_ues_mac_t matched_ues = {0}; - matched_ues.num_ues = filter_ues_by_s_nssai_in_du_or_monolithic(test_cond, sst, sd, &matched_ues); - if (matched_ues.num_ues == 0) return false; - kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_monolithic(&matched_ues, &frm_4->action_def_format_1); - } else if (RC.nrrrc[0]->node_type == ngran_gNB_CUCP) { - return false; // at the moment, measurements regarding CU-CP node are not yet implemented - } else { - assert(false && "NG-RAN Type not implemented"); - } - - - #elif defined (NGRAN_GNB_CUUP) - matched_ues_pdcp_t matched_ues = {0}; - matched_ues.num_ues = filter_ues_by_s_nssai_in_cuup(test_cond, sst, sd, &matched_ues); - if (matched_ues.num_ues == 0) return false; - kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_cuup(&matched_ues, &frm_4->action_def_format_1); - #endif - - - free(sd); // if NULL, nothing happens - - break; - } - - default: - assert(false && "Unknown Test condition"); - } + if (arr_ue_id.sz == 0) return false; + kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3(&arr_ue_id, &frm_4->action_def_format_1); } - break; } @@ -866,13 +419,117 @@ bool read_kpm_sm(void* data) return true; } +static const char* kpm_meas_du[] = { + "DRB.RlcSduDelayDl", + "DRB.UEThpDl", + "DRB.UEThpUl", + "RRU.PrbTotDl", + "RRU.PrbTotUl", +}; + +static const char* kpm_meas_gnb[] = { + "DRB.PdcpSduVolumeDL", + "DRB.PdcpSduVolumeUL", + "DRB.RlcSduDelayDl", + "DRB.UEThpDl", + "DRB.UEThpUl", + "RRU.PrbTotDl", + "RRU.PrbTotUl", +}; + +static const char* kpm_meas_cuup[] = { + "DRB.PdcpSduVolumeDL", + "DRB.PdcpSduVolumeUL", +}; + +typedef const char** meas_list; + +static const meas_list ran_def_kpm[END_NGRAN_NODE_TYPE] = { + NULL, + NULL, + kpm_meas_gnb, + NULL, + NULL, + kpm_meas_cuup, // at the moment, for CU, we use the same function as for CU-UP + NULL, + kpm_meas_du, + NULL, + NULL, // at the moment, no measurement is implemented in CU-CP + kpm_meas_cuup, +}; + void read_kpm_setup_sm(void* e2ap) { assert(e2ap != NULL); // assert(e2ap->type == KPM_V3_0_AGENT_IF_E2_SETUP_ANS_V0); kpm_e2_setup_t* kpm = (kpm_e2_setup_t*)(e2ap); - kpm->ran_func_def = fill_rnd_kpm_ran_func_def(); + + /* Fill the RAN Function Definition with currently supported measurements */ + + // RAN Function Name is already filled in fill_ran_function_name() in kpm_sm_agent.c + + // Fill supported measurements depending on the E2 node + // [1, 65535] + // 3GPP TS 28.552 + const ngran_node_t node_type = get_e2_node_type(); + const char** kpm_meas = ran_def_kpm[node_type]; + if (kpm_meas == NULL) return; // e.g. CU-CP node, doesn't support any measurements + size_t sz = 0; + while (kpm_meas[sz] != NULL) { + sz++; + } + + // Sequence of Report styles + const size_t sz_report = 1; + kpm->ran_func_def.sz_ric_report_style_list = sz_report; + kpm->ran_func_def.ric_report_style_list = calloc(sz_report, sizeof(ric_report_style_item_t)); + assert(kpm->ran_func_def.ric_report_style_list != NULL && "Memory exhausted"); + + ric_report_style_item_t* report_item = &kpm->ran_func_def.ric_report_style_list[0]; + + report_item->report_style_type = STYLE_4_RIC_SERVICE_REPORT; + const char report_style_name[] = "Common Condition-based, UE-level Measurement"; + report_item->report_style_name = cp_str_to_ba(report_style_name); + report_item->act_def_format_type = FORMAT_4_ACTION_DEFINITION; + + report_item->meas_info_for_action_lst_len = sz; + report_item->meas_info_for_action_lst = calloc(sz, sizeof(meas_info_for_action_lst_t)); + assert(report_item->meas_info_for_action_lst != NULL && "Memory exhausted"); + + for(size_t i = 0; i < sz; i++) { + meas_info_for_action_lst_t* meas_item = &report_item->meas_info_for_action_lst[i]; + // 8.3.9 + meas_item->name = cp_str_to_ba(kpm_meas[i]); + + // 8.3.10 - OPTIONAL + meas_item->id = NULL; + + // 8.3.26 - OPTIONAL + meas_item->bin_range_def = NULL; + } + + // Sequence of Event Trigger styles + const size_t sz_ev_tr = 1; + kpm->ran_func_def.sz_ric_event_trigger_style_list = sz_ev_tr; + kpm->ran_func_def.ric_event_trigger_style_list = calloc(sz_ev_tr, sizeof(ric_event_trigger_style_item_t)); + assert(kpm->ran_func_def.ric_event_trigger_style_list != NULL && "Memory exhausted"); + + ric_event_trigger_style_item_t* ev_tr_item = &kpm->ran_func_def.ric_event_trigger_style_list[0]; + + ev_tr_item->style_type = STYLE_1_RIC_EVENT_TRIGGER; + const char ev_style_name[] = "Periodic Report"; + ev_tr_item->style_name = cp_str_to_ba(ev_style_name); + ev_tr_item->format_type = FORMAT_1_RIC_EVENT_TRIGGER; + + // Supported RIC Indication Formats + report_item->ind_hdr_format_type = FORMAT_1_INDICATION_HEADER; // 8.3.5 + report_item->ind_msg_format_type = FORMAT_3_INDICATION_MESSAGE; // 8.3.5 + + // E2 Setup Request is sent periodically until the connection is established + // RC subscritpion data should be initialized only once + const int ret = pthread_once(&once_kpm_mutex, init_once_kpm); + DevAssert(ret == 0); } sm_ag_if_ans_t write_ctrl_kpm_sm(void const* src) diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c new file mode 100644 index 0000000000..80579e1754 --- /dev/null +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.c @@ -0,0 +1,232 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "ran_func_kpm_subs.h" + +#include + +/* measurements that need to store values from previous reporting period have a limitation + when it comes to multiple subscriptions to the same UEs; ric_req_id is unique per subscription */ +typedef struct uldlcounter { + uint32_t dl; + uint32_t ul; +} uldlcounter_t; + +static uldlcounter_t last_pdcp_sdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; + +static nr_pdcp_statistics_t get_pdcp_stats_per_drb(const uint32_t rrc_ue_id, const int rb_id) +{ + nr_pdcp_statistics_t pdcp = {0}; + const int srb_flag = 0; + + // Get PDCP stats for specific DRB + const bool rc = nr_pdcp_get_statistics(rrc_ue_id, srb_flag, rb_id, &pdcp); + assert(rc == true && "Cannot get PDCP stats\n"); + + return pdcp; +} + +/* 3GPP TS 28.522 - section 5.1.2.1.1.1 + note: this measurement is calculated as per spec */ +static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +{ + meas_record_lst_t meas_record = {0}; + + // Get PDCP stats per DRB + const int rb_id = 1; // at the moment, only 1 DRB is supported + nr_pdcp_statistics_t pdcp = get_pdcp_stats_per_drb(ue_info.rrc_ue_id, rb_id); + + meas_record.value = INTEGER_MEAS_VALUE; + + // Get DL data volume delivered to PDCP layer + meas_record.int_val = (pdcp.rxsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].dl)*8/1000; // [kb] + last_pdcp_sdu_total_bytes[ue_idx].dl = pdcp.rxsdu_bytes; + + return meas_record; +} + +/* 3GPP TS 28.522 - section 5.1.2.1.2.1 + note: this measurement is calculated as per spec */ +static meas_record_lst_t fill_DRB_PdcpSduVolumeUL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +{ + meas_record_lst_t meas_record = {0}; + + // Get PDCP stats per DRB + const int rb_id = 1; // at the moment, only 1 DRB is supported + nr_pdcp_statistics_t pdcp = get_pdcp_stats_per_drb(ue_info.rrc_ue_id, rb_id); + + meas_record.value = INTEGER_MEAS_VALUE; + + // Get UL data volume delivered from PDCP layer + meas_record.int_val = (pdcp.txsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].ul)*8/1000; // [kb] + last_pdcp_sdu_total_bytes[ue_idx].ul = pdcp.txsdu_bytes; + + return meas_record; +} + +#if defined (NGRAN_GNB_DU) +static uldlcounter_t last_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0}; +static uldlcounter_t last_total_prbs[MAX_MOBILES_PER_GNB] = {0}; + +static nr_rlc_statistics_t get_rlc_stats_per_drb(const rnti_t rnti, const int rb_id) +{ + nr_rlc_statistics_t rlc = {0}; + const int srb_flag = 0; + + // Get RLC stats for specific DRB + const bool rc = nr_rlc_get_statistics(rnti, srb_flag, rb_id, &rlc); + assert(rc == true && "Cannot get RLC stats\n"); + + // Activate average sojourn time at the RLC buffer for specific DRB + nr_rlc_activate_avg_time_to_tx(rnti, rb_id+3, 1); + + return rlc; +} + +/* 3GPP TS 28.522 - section 5.1.3.3.3 + note: by default this measurement is calculated for previous 100ms (openair2/LAYER2/nr_rlc/nr_rlc_entity.c:118, 173, 213); please, update according to your needs */ +static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, __attribute__((unused))const size_t ue_idx) +{ + meas_record_lst_t meas_record = {0}; + + // Get RLC stats per DRB + const int rb_id = 1; // at the moment, only 1 DRB is supported + nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(ue_info.ue->rnti, rb_id); + + meas_record.value = REAL_MEAS_VALUE; + + // Get the value of sojourn time at the RLC buffer + meas_record.real_val = rlc.txsdu_avg_time_to_tx; // [μs] + + return meas_record; +} + +/* 3GPP TS 28.522 - section 5.1.1.3.1 + note: per spec, average UE throughput in DL (taken into consideration values from all UEs, and averaged) + here calculated as: UE specific throughput in DL */ +static meas_record_lst_t fill_DRB_UEThpDl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +{ + meas_record_lst_t meas_record = {0}; + + // Get RLC stats per DRB + const int rb_id = 1; // at the moment, only 1 DRB is supported + nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(ue_info.ue->rnti, rb_id); + meas_record.value = REAL_MEAS_VALUE; + + // Calculate DL Thp + meas_record.real_val = (double)(rlc.txpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].dl)*8/gran_period_ms; // [kbps] + last_rlc_pdu_total_bytes[ue_idx].dl = rlc.txpdu_bytes; + + return meas_record; +} + +/* 3GPP TS 28.522 - section 5.1.1.3.3 + note: per spec, average UE throughput in UL (taken into consideration values from all UEs, and averaged) + here calculated as: UE specific throughput in UL */ +static meas_record_lst_t fill_DRB_UEThpUl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +{ + meas_record_lst_t meas_record = {0}; + + // Get RLC stats per DRB + const int rb_id = 1; // at the moment, only 1 DRB is supported + nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(ue_info.ue->rnti, rb_id); + + meas_record.value = REAL_MEAS_VALUE; + + // Calculate UL Thp + meas_record.real_val = (double)(rlc.rxpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].ul)*8/gran_period_ms; // [kbps] + last_rlc_pdu_total_bytes[ue_idx].ul = rlc.rxpdu_bytes; + + return meas_record; +} + +/* 3GPP TS 28.522 - section 5.1.1.2.1 + note: per spec, DL PRB usage [%] = (total used PRBs for DL traffic / total available PRBs for DL traffic) * 100 + here calculated as: aggregated DL PRBs (t) - aggregated DL PRBs (t-gran_period) */ +static meas_record_lst_t fill_RRU_PrbTotDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +{ + meas_record_lst_t meas_record = {0}; + + meas_record.value = INTEGER_MEAS_VALUE; + + // Get the number of DL PRBs + meas_record.int_val = ue_info.ue->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl; // [PRBs] + last_total_prbs[ue_idx].dl = ue_info.ue->mac_stats.dl.total_rbs; + + return meas_record; +} + +/* 3GPP TS 28.522 - section 5.1.1.2.2 + note: per spec, UL PRB usage [%] = (total used PRBs for UL traffic / total available PRBs for UL traffic) * 100 + here calculated as: aggregated UL PRBs (t) - aggregated UL PRBs (t-gran_period) */ +static meas_record_lst_t fill_RRU_PrbTotUl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +{ + meas_record_lst_t meas_record = {0}; + + meas_record.value = INTEGER_MEAS_VALUE; + + // Get the number of UL PRBs + meas_record.int_val = ue_info.ue->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul; // [PRBs] + last_total_prbs[ue_idx].ul = ue_info.ue->mac_stats.ul.total_rbs; + + return meas_record; +} +#endif + +static kv_measure_t lst_measure[] = { + {.key = "DRB.PdcpSduVolumeDL", .value = fill_DRB_PdcpSduVolumeDL }, + {.key = "DRB.PdcpSduVolumeUL", .value = fill_DRB_PdcpSduVolumeUL }, +#if defined (NGRAN_GNB_DU) + {.key = "DRB.RlcSduDelayDl", .value = fill_DRB_RlcSduDelayDl }, + {.key = "DRB.UEThpDl", .value = fill_DRB_UEThpDl }, + {.key = "DRB.UEThpUl", .value = fill_DRB_UEThpUl }, + {.key = "RRU.PrbTotDl", .value = fill_RRU_PrbTotDl }, + {.key = "RRU.PrbTotUl", .value = fill_RRU_PrbTotUl }, +#endif +}; + +void init_kpm_subs_data(void) +{ + const size_t ht_len = sizeof(lst_measure) / sizeof(lst_measure[0]); + hcreate(ht_len); + + ENTRY kv_pair; + + for (size_t i = 0; i < ht_len; i++) { + kv_pair.key = lst_measure[i].key; + kv_pair.data = &lst_measure[i]; + hsearch(kv_pair, ENTER); + } +} + +meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx) +{ + assert(kpm_meas_name != NULL); + + ENTRY search_entry = {.key = kpm_meas_name}; + ENTRY *found_entry = hsearch(search_entry, FIND); + assert(found_entry != NULL && "Unsupported KPM measurement name"); + + kv_measure_t *kv_found = (kv_measure_t *)found_entry->data; + meas_record_lst_t meas_record = kv_found->value(gran_period_ms, ue_info, ue_idx); + + return meas_record; +} diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.h b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.h new file mode 100644 index 0000000000..7dbd807fa1 --- /dev/null +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm_subs.h @@ -0,0 +1,49 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef RAN_FUNC_SM_KPM_SUBSCRIPTION_AGENT_H +#define RAN_FUNC_SM_KPM_SUBSCRIPTION_AGENT_H + +#include "openair2/E2AP/flexric/src/sm/kpm_sm/kpm_data_ie_wrapper.h" + +#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" +#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h" +#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" + +typedef struct { + uint32_t rrc_ue_id; + NR_UE_info_t* ue; + +} cudu_ue_info_pair_t; + +typedef meas_record_lst_t (*kpm_meas_fp)(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx); + +typedef struct{ + char* key; + kpm_meas_fp value; +} kv_measure_t; + + +void init_kpm_subs_data(void); + +meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx); + +#endif diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c index 7cf3e94f64..ef23bf2fd4 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c @@ -22,7 +22,7 @@ #include "ran_func_rc.h" #include "ran_func_rc_subs.h" #include "ran_func_rc_extern.h" -#include "../../flexric/test/rnd/fill_rnd_data_rc.h" // this dependancy will be taken out once RAN Function Definition is implemented +#include "ran_e2sm_ue_id.h" #include "../../flexric/src/sm/rc_sm/ie/ir/lst_ran_param.h" #include "../../flexric/src/sm/rc_sm/ie/ir/ran_param_list.h" #include "../../flexric/src/agent/e2_agent_api.h" @@ -35,17 +35,440 @@ static pthread_once_t once_rc_mutex = PTHREAD_ONCE_INIT; static rc_subs_data_t rc_subs_data = {0}; static pthread_mutex_t rc_mutex = PTHREAD_MUTEX_INITIALIZER; +static ngran_node_t get_e2_node_type(void) +{ + ngran_node_t node_type = 0; + +#if defined(NGRAN_GNB_DU) && defined(NGRAN_GNB_CUUP) && defined(NGRAN_GNB_CUCP) + node_type = RC.nrrrc[0]->node_type; +#elif defined (NGRAN_GNB_CUUP) + node_type = ngran_gNB_CUUP; +#endif + + return node_type; +} + static void init_once_rc(void) { init_rc_subs_data(&rc_subs_data); } +static void fill_rc_ev_trig(ran_func_def_ev_trig_t* ev_trig) +{ + // Sequence of EVENT TRIGGER styles + // [1 - 63] + ev_trig->sz_seq_ev_trg_style = 1; + ev_trig->seq_ev_trg_style = calloc(ev_trig->sz_seq_ev_trg_style, sizeof(seq_ev_trg_style_t)); + assert(ev_trig->seq_ev_trg_style != NULL && "Memory exhausted"); + + seq_ev_trg_style_t* ev_trig_style = &ev_trig->seq_ev_trg_style[0]; + + // RIC Event Trigger Style Type + // Mandatory + // 9.3.3 + // 6.2.2.2. + // INTEGER + ev_trig_style->style = 4; + + // RIC Event Trigger Style Name + // Mandatory + // 9.3.4 + // 6.2.2.3 + //PrintableString(SIZE(1..150,...)) + const char ev_style_name[] = "UE Information Change"; + ev_trig_style->name = cp_str_to_ba(ev_style_name); + + // RIC Event Trigger Format Type + // Mandatory + // 9.3.5 + // 6.2.2.4. + // INTEGER + ev_trig_style->format = FORMAT_4_E2SM_RC_EV_TRIGGER_FORMAT; + + // Sequence of RAN Parameters for L2 Variables + // [0 - 65535] + ev_trig->sz_seq_ran_param_l2_var = 0; + ev_trig->seq_ran_param_l2_var = NULL; + + //Sequence of Call Process Types + // [0-65535] + ev_trig->sz_seq_call_proc_type = 0; + ev_trig->seq_call_proc_type = NULL; + + // Sequence of RAN Parameters for Identifying UEs + // 0-65535 + ev_trig->sz_seq_ran_param_id_ue = 0; + ev_trig->seq_ran_param_id_ue = NULL; + + // Sequence of RAN Parameters for Identifying Cells + // 0-65535 + ev_trig->sz_seq_ran_param_id_cell = 0; + ev_trig->seq_ran_param_id_cell = NULL; +} + +static void fill_rc_report(ran_func_def_report_t* report) +{ + // Sequence of REPORT styles + // [1 - 63] + report->sz_seq_report_sty = 1; + report->seq_report_sty = calloc(report->sz_seq_report_sty, sizeof(seq_report_sty_t)); + assert(report->seq_report_sty != NULL && "Memory exhausted"); + + seq_report_sty_t* report_style = &report->seq_report_sty[0]; + + // RIC Report Style Type + // Mandatory + // 9.3.3 + // 6.2.2.2. + // INTEGER + report_style->report_type = 4; + + // RIC Report Style Name + // Mandatory + // 9.3.4 + // 6.2.2.3. + // PrintableString(SIZE(1..150,...)) + const char report_name[] = "UE Information"; + report_style->name = cp_str_to_ba(report_name); + + // Supported RIC Event Trigger Style Type + // Mandatory + // 9.3.3 + // 6.2.2.2. + // INTEGER + report_style->ev_trig_type = FORMAT_4_E2SM_RC_EV_TRIGGER_FORMAT; + + // RIC Report Action Format Type + // Mandatory + // 9.3.5 + // 6.2.2.4. + // INTEGER + report_style->act_frmt_type = FORMAT_1_E2SM_RC_ACT_DEF; + + // RIC Indication Header Format Type + // Mandatory + // 9.3.5 + // 6.2.2.4. + // INTEGER + report_style->ind_hdr_type = FORMAT_1_E2SM_RC_IND_HDR; + + // RIC Indication Message Format Type + // Mandatory + // 9.3.5 + // 6.2.2.4. + // INTEGER + report_style->ind_msg_type = FORMAT_2_E2SM_RC_IND_MSG; + + // Sequence of RAN Parameters Supported + // [0 - 65535] + report_style->sz_seq_ran_param = 1; + report_style->ran_param = calloc(report_style->sz_seq_ran_param, sizeof(seq_ran_param_3_t)); + assert(report_style->ran_param != NULL && "Memory exhausted"); + + // RAN Parameter ID + // Mandatory + // 9.3.8 + // [1- 4294967295] + report_style->ran_param[0].id = RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID; + + // RAN Parameter Name + // Mandatory + // 9.3.9 + // [1-150] + const char ran_param_name[] = "RRC State"; + report_style->ran_param[0].name = cp_str_to_ba(ran_param_name); + + // RAN Parameter Definition + // Optional + // 9.3.51 + report_style->ran_param[0].def = NULL; +} + +static void fill_rc_control(ran_func_def_ctrl_t* ctrl) +{ + // Sequence of CONTROL styles + // [1 - 63] + ctrl->sz_seq_ctrl_style = 1; + ctrl->seq_ctrl_style = calloc(ctrl->sz_seq_ctrl_style, sizeof(seq_ctrl_style_t)); + assert(ctrl->seq_ctrl_style != NULL && "Memory exhausted"); + + seq_ctrl_style_t* ctrl_style = &ctrl->seq_ctrl_style[0]; + + // RIC Control Style Type + // Mandatory + // 9.3.3 + // 6.2.2.2 + ctrl_style->style_type = 1; + + //RIC Control Style Name + //Mandatory + //9.3.4 + // [1 -150] + const char control_name[] = "Radio Bearer Control"; + ctrl_style->name = cp_str_to_ba(control_name); + + // RIC Control Header Format Type + // Mandatory + // 9.3.5 + ctrl_style->hdr = FORMAT_1_E2SM_RC_CTRL_HDR; + + // RIC Control Message Format Type + // Mandatory + // 9.3.5 + ctrl_style->msg = FORMAT_1_E2SM_RC_CTRL_MSG; + + // RIC Call Process ID Format Type + // Optional + ctrl_style->call_proc_id_type = NULL; + + // RIC Control Outcome Format Type + // Mandatory + // 9.3.5 + ctrl_style->out_frmt = FORMAT_1_E2SM_RC_CTRL_OUT; + + // Sequence of Control Actions + // [0-65535] + ctrl_style->sz_seq_ctrl_act = 1; + ctrl_style->seq_ctrl_act = calloc(ctrl_style->sz_seq_ctrl_act, sizeof(seq_ctrl_act_2_t)); + assert(ctrl_style->seq_ctrl_act != NULL && "Memory exhausted"); + + seq_ctrl_act_2_t* ctrl_act = &ctrl_style->seq_ctrl_act[0]; + + // Control Action ID + // Mandatory + // 9.3.6 + // [1-65535] + ctrl_act->id = 2; + + // Control Action Name + // Mandatory + // 9.3.7 + // [1-150] + const char control_act_name[] = "QoS flow mapping configuration"; + ctrl_act->name = cp_str_to_ba(control_act_name); + + // Sequence of Associated RAN Parameters + // [0-65535] + ctrl_act->sz_seq_assoc_ran_param = 2; + ctrl_act->assoc_ran_param = calloc(ctrl_act->sz_seq_assoc_ran_param, sizeof(seq_ran_param_3_t)); + assert(ctrl_act->assoc_ran_param != NULL && "Memory exhausted"); + + seq_ran_param_3_t* assoc_ran_param = ctrl_act->assoc_ran_param; + + // DRB ID + assoc_ran_param[0].id = 1; + const char ran_param_drb_id[] = "DRB ID"; + assoc_ran_param[0].name = cp_str_to_ba(ran_param_drb_id); + assoc_ran_param[0].def = NULL; + + // List of QoS Flows to be modified in DRB + assoc_ran_param[1].id = 2; + const char ran_param_list_qos_flow[] = "List of QoS Flows to be modified in DRB"; + assoc_ran_param[1].name = cp_str_to_ba(ran_param_list_qos_flow); + assoc_ran_param[1].def = calloc(1, sizeof(ran_param_def_t)); + assert(assoc_ran_param[1].def != NULL && "Memory exhausted"); + + assoc_ran_param[1].def->type = LIST_RAN_PARAMETER_DEF_TYPE; + assoc_ran_param[1].def->lst = calloc(1, sizeof(ran_param_type_t)); + assert(assoc_ran_param[1].def->lst != NULL && "Memory exhausted"); + + ran_param_type_t* lst = assoc_ran_param[1].def->lst; + lst->sz_ran_param = 2; + lst->ran_param = calloc(lst->sz_ran_param, sizeof(ran_param_lst_struct_t)); + assert(lst->ran_param != NULL && "Memory exhausted"); + + // QoS Flow Identifier + lst->ran_param[0].ran_param_id = 4; + const char ran_param_qos_flow_id[] = "QoS Flow Identifier"; + lst->ran_param[0].ran_param_name = cp_str_to_ba(ran_param_qos_flow_id); + lst->ran_param[0].ran_param_def = NULL; + + // QoS Flow Mapping Indication + lst->ran_param[1].ran_param_id = 5; + const char ran_param_qos_flow_mapping_ind[] = "QoS Flow Mapping Indication"; + lst->ran_param[1].ran_param_name = cp_str_to_ba(ran_param_qos_flow_mapping_ind); + lst->ran_param[1].ran_param_def = NULL; + + // Sequence of Associated RAN + // Parameters for Control Outcome + // [0- 255] + ctrl_style->sz_ran_param_ctrl_out = 0; + ctrl_style->ran_param_ctrl_out = NULL; +} + +e2sm_rc_func_def_t fill_rc_ran_def_gnb(void) +{ + e2sm_rc_func_def_t def = {0}; + + // RAN Function Definition for EVENT TRIGGER + // Optional + // 9.2.2.2 + def.ev_trig = calloc(1, sizeof(ran_func_def_ev_trig_t)); + assert(def.ev_trig != NULL && "Memory exhausted"); + fill_rc_ev_trig(def.ev_trig); + + // RAN Function Definition for REPORT + // Optional + // 9.2.2.3 + def.report = calloc(1, sizeof(ran_func_def_report_t)); + assert(def.report != NULL && "Memory exhausted"); + fill_rc_report(def.report); + + // RAN Function Definition for INSERT + // Optional + // 9.2.2.4 + def.insert = NULL; + + // RAN Function Definition for CONTROL + // Optional + // 9.2.2.5 + def.ctrl = calloc(1, sizeof(ran_func_def_ctrl_t)); + assert(def.ctrl != NULL && "Memory exhausted"); + fill_rc_control(def.ctrl); + + // RAN Function Definition for POLICY + // Optional + // 9.2.2.6 + def.policy = NULL; + + return def; +} + +static e2sm_rc_func_def_t fill_rc_ran_def_cu(void) +{ + e2sm_rc_func_def_t def = {0}; + + // RAN Function Definition for EVENT TRIGGER + // Optional + // 9.2.2.2 + def.ev_trig = calloc(1, sizeof(ran_func_def_ev_trig_t)); + assert(def.ev_trig != NULL && "Memory exhausted"); + fill_rc_ev_trig(def.ev_trig); + + // RAN Function Definition for REPORT + // Optional + // 9.2.2.3 + def.report = calloc(1, sizeof(ran_func_def_report_t)); + assert(def.report != NULL && "Memory exhausted"); + fill_rc_report(def.report); + + // RAN Function Definition for INSERT + // Optional + // 9.2.2.4 + def.insert = NULL; + + // RAN Function Definition for CONTROL + // Optional + // 9.2.2.5 + def.ctrl = calloc(1, sizeof(ran_func_def_ctrl_t)); + assert(def.ctrl != NULL && "Memory exhausted"); + fill_rc_control(def.ctrl); + + // RAN Function Definition for POLICY + // Optional + // 9.2.2.6 + def.policy = NULL; + + return def; +} + +static e2sm_rc_func_def_t fill_rc_ran_def_null(void) +{ + e2sm_rc_func_def_t def = {0}; + + // RAN Function Definition for EVENT TRIGGER + // Optional + // 9.2.2.2 + def.ev_trig = NULL; + + // RAN Function Definition for REPORT + // Optional + // 9.2.2.3 + def.report = NULL; + + // RAN Function Definition for INSERT + // Optional + // 9.2.2.4 + def.insert = NULL; + + // RAN Function Definition for CONTROL + // Optional + // 9.2.2.5 + def.ctrl = NULL; + + // RAN Function Definition for POLICY + // Optional + // 9.2.2.6 + def.policy = NULL; + + return def; +} + +static e2sm_rc_func_def_t fill_rc_ran_def_cucp(void) +{ + e2sm_rc_func_def_t def = {0}; + + // RAN Function Definition for EVENT TRIGGER + // Optional + // 9.2.2.2 + def.ev_trig = calloc(1, sizeof(ran_func_def_ev_trig_t)); + assert(def.ev_trig != NULL && "Memory exhausted"); + fill_rc_ev_trig(def.ev_trig); + + // RAN Function Definition for REPORT + // Optional + // 9.2.2.3 + def.report = calloc(1, sizeof(ran_func_def_report_t)); + assert(def.report != NULL && "Memory exhausted"); + fill_rc_report(def.report); + + // RAN Function Definition for INSERT + // Optional + // 9.2.2.4 + def.insert = NULL; + + // RAN Function Definition for CONTROL + // Optional + // 9.2.2.5 + def.ctrl = NULL; + + // RAN Function Definition for POLICY + // Optional + // 9.2.2.6 + def.policy = NULL; + + return def; +} + +typedef e2sm_rc_func_def_t (*fp_rc_func_def)(void); + +static const fp_rc_func_def ran_def_rc[END_NGRAN_NODE_TYPE] = +{ + NULL, + NULL, + fill_rc_ran_def_gnb, + NULL, + NULL, + fill_rc_ran_def_cu, + NULL, + fill_rc_ran_def_null, // DU - at the moment, no Service is supported + NULL, + fill_rc_ran_def_cucp, + fill_rc_ran_def_null, // CU-UP - at the moment, no Service is supported +}; + void read_rc_setup_sm(void* data) { assert(data != NULL); // assert(data->type == RAN_CTRL_V1_3_AGENT_IF_E2_SETUP_ANS_V0); rc_e2_setup_t* rc = (rc_e2_setup_t*)data; - rc->ran_func_def = fill_rc_ran_func_def(); + + /* Fill the RAN Function Definition with currently supported measurements */ + + // RAN Function Name is already filled in fill_ran_function_name() in rc_sm_agent.c + + const ngran_node_t node_type = get_e2_node_type(); + rc->ran_func_def = ran_def_rc[node_type](); // E2 Setup Request is sent periodically until the connection is established // RC subscritpion data should be initialized only once @@ -56,53 +479,6 @@ void read_rc_setup_sm(void* data) RB_PROTOTYPE(ric_id_2_param_id_trees, ric_req_id_s, entries, cmp_ric_req_id); -static ue_id_e2sm_t fill_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context) -{ - ue_id_e2sm_t ue_id = {0}; - - // Check the E2 node type - #if defined (NGRAN_GNB_CUUP) && defined (NGRAN_GNB_CUCP) - ue_id.type = GNB_UE_ID_E2SM; - if (RC.nrrrc[0]->node_type == ngran_gNB_CU) { - // gNB-CU UE F1AP ID List - // C-ifCUDUseparated - ue_id.gnb.gnb_cu_ue_f1ap_lst_len = 1; - ue_id.gnb.gnb_cu_ue_f1ap_lst = calloc(ue_id.gnb.gnb_cu_ue_f1ap_lst_len, sizeof(uint32_t)); - assert(ue_id.gnb.gnb_cu_ue_f1ap_lst != NULL && "Memory exhausted"); - - for(size_t i = 0; i < ue_id.gnb.gnb_cu_ue_f1ap_lst_len; i++) { - ue_id.gnb.gnb_cu_ue_f1ap_lst[i] = rrc_ue_context->rrc_ue_id; - } - } else if (RC.nrrrc[0]->node_type == ngran_gNB_CUCP) { - //gNB-CU-CP UE E1AP ID List - //C-ifCPUPseparated - ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len = 1; - ue_id.gnb.gnb_cu_cp_ue_e1ap_lst = calloc(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len, sizeof(uint32_t)); - assert(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst != NULL && "Memory exhausted"); - - for(size_t i = 0; i < ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len; i++) { - ue_id.gnb.gnb_cu_cp_ue_e1ap_lst[i] = rrc_ue_context->rrc_ue_id; - } - } else if (RC.nrrrc[0]->node_type == ngran_gNB_DU) { - assert(false && "RRC state change cannot be triggered from DU\n"); - } - - #elif defined (NGRAN_GNB_CUUP) - assert(false && "RRC state change cannot be triggered from CU-UP\n"); - - #endif - - ue_id.gnb.amf_ue_ngap_id = rrc_ue_context->rrc_ue_id; - ue_id.gnb.guami.amf_ptr = rrc_ue_context->ue_guami.amf_pointer; - ue_id.gnb.guami.amf_region_id = rrc_ue_context->ue_guami.amf_region_id; - ue_id.gnb.guami.amf_set_id = rrc_ue_context->ue_guami.amf_set_id; - ue_id.gnb.guami.plmn_id.mcc = rrc_ue_context->ue_guami.mcc; - ue_id.gnb.guami.plmn_id.mnc = rrc_ue_context->ue_guami.mnc; - ue_id.gnb.guami.plmn_id.mnc_digit_len = rrc_ue_context->ue_guami.mnc_len; - - return ue_id; -} - static seq_ran_param_t fill_rrc_state_change_seq_ran(const rc_sm_rrc_state_e rrc_state) { seq_ran_param_t seq_ran_param = {0}; @@ -138,7 +514,8 @@ static rc_ind_data_t* fill_ue_rrc_state_change(const gNB_RRC_UE_t *rrc_ue_contex // UE ID // Mandatory // 9.3.10 - rc_ind->msg.frmt_2.seq_ue_id[0].ue_id = fill_ue_id_data(rrc_ue_context); + const ngran_node_t node_type = get_e2_node_type(); + rc_ind->msg.frmt_2.seq_ue_id[0].ue_id = fill_ue_id_data[node_type](rrc_ue_context, 0, 0); // Sequence of // RAN Parameter @@ -221,39 +598,45 @@ sm_ag_if_ans_t write_ctrl_rc_sm(void const* data) // assert(data->type == RAN_CONTROL_CTRL_V1_03 ); rc_ctrl_req_data_t const* ctrl = (rc_ctrl_req_data_t const*)data; - if(ctrl->hdr.format == FORMAT_1_E2SM_RC_CTRL_HDR){ - if(ctrl->hdr.frmt_1.ric_style_type == 1 && ctrl->hdr.frmt_1.ctrl_act_id == 2){ - printf("QoS flow mapping configuration \n"); - e2sm_rc_ctrl_msg_frmt_1_t const* frmt_1 = &ctrl->msg.frmt_1; - for(size_t i = 0; i < frmt_1->sz_ran_param; ++i){ - seq_ran_param_t const* rp = frmt_1->ran_param; - if(rp[i].ran_param_id == 1){ - assert(rp[i].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE ); - printf("DRB ID %ld \n", rp[i].ran_param_val.flag_true->int_ran); - } else if(rp[i].ran_param_id == 2){ - assert(rp[i].ran_param_val.type == LIST_RAN_PARAMETER_VAL_TYPE); - printf("List of QoS Flows to be modified \n"); - for(size_t j = 0; j < ctrl->msg.frmt_1.ran_param[i].ran_param_val.lst->sz_lst_ran_param; ++j){ - lst_ran_param_t const* lrp = rp[i].ran_param_val.lst->lst_ran_param; - // The following assertion should be true, but there is a bug in the std - // check src/sm/rc_sm/enc/rc_enc_asn.c:1085 and src/sm/rc_sm/enc/rc_enc_asn.c:984 - // assert(lrp[j].ran_param_id == 3); - assert(lrp[j].ran_param_struct.ran_param_struct[0].ran_param_id == 4) ; - assert(lrp[j].ran_param_struct.ran_param_struct[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE); - int64_t qfi = lrp[j].ran_param_struct.ran_param_struct[0].ran_param_val.flag_true->int_ran; - assert(qfi > -1 && qfi < 65); + assert(ctrl->hdr.format == FORMAT_1_E2SM_RC_CTRL_HDR && "Indication Header Format received not valid"); + assert(ctrl->msg.format == FORMAT_1_E2SM_RC_CTRL_MSG && "Indication Message Format received not valid"); + assert(ctrl->hdr.frmt_1.ctrl_act_id == 2 && "Currently only QoS flow mapping configuration supported"); + + printf("QoS flow mapping configuration\n"); + + const seq_ran_param_t* ran_param = ctrl->msg.frmt_1.ran_param; + + // DRB ID + assert(ran_param[0].ran_param_id == 1 && "First RAN Parameter ID has to be DRB ID"); + assert(ran_param[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE); + printf("DRB ID %ld \n", ran_param[0].ran_param_val.flag_true->int_ran); + + + // List of QoS Flows to be modified in DRB + assert(ran_param[1].ran_param_id == 2 && "Second RAN Parameter ID has to be List of QoS Flows"); + assert(ran_param[1].ran_param_val.type == LIST_RAN_PARAMETER_VAL_TYPE); + printf("List of QoS Flows to be modified in DRB\n"); + const lst_ran_param_t* lrp = ran_param[1].ran_param_val.lst->lst_ran_param; + + // The following assertion should be true, but there is a bug in the std + // check src/sm/rc_sm/enc/rc_enc_asn.c:1085 and src/sm/rc_sm/enc/rc_enc_asn.c:984 + // assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_id == 3); + + // QoS Flow Identifier + assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_id == 4); + assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE); + int64_t qfi = lrp->ran_param_struct.ran_param_struct[0].ran_param_val.flag_true->int_ran; + assert(qfi > -1 && qfi < 65); + + // QoS Flow Mapping Indication + assert(lrp->ran_param_struct.ran_param_struct[1].ran_param_id == 5); + assert(lrp->ran_param_struct.ran_param_struct[1].ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE); + int64_t dir = lrp->ran_param_struct.ran_param_struct[1].ran_param_val.flag_false->int_ran; + assert(dir == 0 || dir == 1); + + printf("qfi = %ld, dir %ld \n", qfi, dir); - assert(lrp[j].ran_param_struct.ran_param_struct[1].ran_param_id == 5); - assert(lrp[j].ran_param_struct.ran_param_struct[1].ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE); - int64_t dir = lrp[j].ran_param_struct.ran_param_struct[1].ran_param_val.flag_false->int_ran; - assert(dir == 0 || dir == 1); - printf("qfi = %ld dir %ld \n", qfi, dir); - } - } - } - } - } sm_ag_if_ans_t ans = {.type = CTRL_OUTCOME_SM_AG_IF_ANS_V0}; ans.ctrl_out.type = RAN_CTRL_V1_3_AGENT_IF_CTRL_ANS_V0; diff --git a/openair2/E2AP/RAN_FUNCTION/read_setup_ran.c b/openair2/E2AP/RAN_FUNCTION/read_setup_ran.c index 774ea290d6..922816f5fc 100644 --- a/openair2/E2AP/RAN_FUNCTION/read_setup_ran.c +++ b/openair2/E2AP/RAN_FUNCTION/read_setup_ran.c @@ -21,10 +21,109 @@ #include "read_setup_ran.h" #include "../../E2AP/flexric/src/lib/e2ap/e2ap_node_component_config_add_wrapper.h" -#include "../../E2AP/flexric/test/rnd/fill_rnd_data_e2_setup_req.h" #include #include +#if defined(E2AP_V2) || defined(E2AP_V3) +// NGAP +static e2ap_node_component_config_add_t fill_ngap_e2ap_node_component_config_add(void) +{ + e2ap_node_component_config_add_t dst = {0}; + + // Mandatory + // 9.2.26 + dst.e2_node_comp_interface_type = NG_E2AP_NODE_COMP_INTERFACE_TYPE; + // Bug!! Optional in the standard, mandatory in ASN.1 + // 9.2.32 + dst.e2_node_comp_id.type = NG_E2AP_NODE_COMP_INTERFACE_TYPE; + + const char ng_msg[] = "Dummy message"; + dst.e2_node_comp_id.ng_amf_name = cp_str_to_ba(ng_msg); + + // Mandatory + // 9.2.27 + const char req[] = "NGAP Request Message sent"; + const char res[] = "NGAP Response Message reveived"; + + dst.e2_node_comp_conf.request = cp_str_to_ba(req); + dst.e2_node_comp_conf.response = cp_str_to_ba(res); + return dst; +} + +// F1AP +static e2ap_node_component_config_add_t fill_f1ap_e2ap_node_component_config_add(void) +{ + e2ap_node_component_config_add_t dst = {0}; + + // Mandatory + // 9.2.26 + dst.e2_node_comp_interface_type = F1_E2AP_NODE_COMP_INTERFACE_TYPE; + // Bug!! Optional in the standard, mandatory in ASN.1 + // 9.2.32 + dst.e2_node_comp_id.type = F1_E2AP_NODE_COMP_INTERFACE_TYPE; + + dst.e2_node_comp_id.f1_gnb_du_id = 1023; + + // Mandatory + // 9.2.27 + const char req[] = "F1AP Request Message sent"; + const char res[] = "F1AP Response Message reveived"; + + dst.e2_node_comp_conf.request = cp_str_to_ba(req); + dst.e2_node_comp_conf.response = cp_str_to_ba(res); + return dst; +} + +// E1AP +static e2ap_node_component_config_add_t fill_e1ap_e2ap_node_component_config_add(void) +{ + e2ap_node_component_config_add_t dst = {0}; + + // Mandatory + // 9.2.26 + dst.e2_node_comp_interface_type = E1_E2AP_NODE_COMP_INTERFACE_TYPE; + // Bug!! Optional in the standard, mandatory in ASN.1 + // 9.2.32 + dst.e2_node_comp_id.type = E1_E2AP_NODE_COMP_INTERFACE_TYPE; + + dst.e2_node_comp_id.e1_gnb_cu_up_id = 1025; + + // Mandatory + // 9.2.27 + const char req[] = "E1AP Request Message sent"; + const char res[] = "E1AP Response Message reveived"; + + dst.e2_node_comp_conf.request = cp_str_to_ba(req); + dst.e2_node_comp_conf.response = cp_str_to_ba(res); + return dst; +} + +// S1AP +static e2ap_node_component_config_add_t fill_s1ap_e2ap_node_component_config_add(void) +{ + e2ap_node_component_config_add_t dst = {0}; + + // Mandatory + // 9.2.26 + dst.e2_node_comp_interface_type = S1_E2AP_NODE_COMP_INTERFACE_TYPE; + // Bug!! Optional in the standard, mandatory in ASN.1 + // 9.2.32 + dst.e2_node_comp_id.type = S1_E2AP_NODE_COMP_INTERFACE_TYPE; + + const char str[] = "S1 NAME"; + dst.e2_node_comp_id.s1_mme_name = cp_str_to_ba(str); + + // Mandatory + // 9.2.27 + const char req[] = "S1AP Request Message sent"; + const char res[] = "S1AP Response Message reveived"; + + dst.e2_node_comp_conf.request = cp_str_to_ba(req); + dst.e2_node_comp_conf.response = cp_str_to_ba(res); + return dst; +} +#endif + void read_setup_ran(void* data, const ngran_node_t node_type) { assert(data != NULL); diff --git a/openair2/E2AP/README.md b/openair2/E2AP/README.md index cf6e8b8cd5..08cdf37067 100644 --- a/openair2/E2AP/README.md +++ b/openair2/E2AP/README.md @@ -13,6 +13,8 @@ Please see [NOTICE](NOTICE.md) file for third party software that is included in This tutorial describes the steps of deployment 5G OAI RAN, with integrated E2 agent and a nearRT-RIC using O-RAN compliant FlexRIC. +[[_TOC_]] + # 1. Installation ## 1.1 Install prerequisites @@ -21,7 +23,7 @@ This tutorial describes the steps of deployment 5G OAI RAN, with integrated E2 a On Ubuntu, you might want to use [this PPA](https://apt.kitware.com/) to install an up-to-date version. -- SWIG (at least v.4.0). +- SWIG (at least v.4.1). We use SWIG as an interface generator to enable the multi-language feature (i.e., C/C++ and Python) for the xApps. Please, check your SWIG version (i.e, `swig -version`) and install it from scratch if necessary as described here: https://swig.org/svn.html or via the code below: @@ -69,17 +71,11 @@ Currently available versions: | E2AP v2.03 | Y | Y | | E2AP v3.01 | Y | Y | - Please note that KPM SM v2.01 is supported only in FlexRIC, but not in OAI. +Please note that KPM SM v2.01 is supported only in FlexRIC, but not in OAI. ```bash cd cmake_targets/ ./build_oai -I -w SIMU --gNB --nrUE --build-e2 --ninja -``` -If the openair2/E2AP/flexric folder is empty, try manually the following commands - -```bash -git submodule init -git submodule update ``` * `-I` option is to install pre-requisites, you only need it the first time you build the softmodem or when some oai dependencies have changed. @@ -89,16 +85,22 @@ git submodule update * `--ninja` is to use the ninja build tool, which speeds up compilation * `--build-e2` option is to use the E2 agent, integrated within RAN. +If the openair2/E2AP/flexric folder is empty, try manually the following commands: +```bash +git submodule init +git submodule update +``` + ## 2.2 FlexRIC -- By default, FlexRIC will build the nearRT-RIC with E2AP v2 and KPM v2. If you want a different version, edit the variable E2AP\_VERSION and KPM\_VERSION at FlexRIC's CMakeLists.txt file. Note that OAI's and FlexRIC's E2AP\_VERSION and KPM\_VERSION need to match due to O-RAN incompatibilities among versions. +By default, FlexRIC will build the nearRT-RIC with E2AP v2 and KPM v2. If you want a different version, edit the variable E2AP\_VERSION and KPM\_VERSION at FlexRIC's CMakeLists.txt file. Note that OAI's and FlexRIC's E2AP\_VERSION and KPM\_VERSION need to match due to O-RAN incompatibilities among versions. ### 2.2.1 Clone the FlexRIC repository ```bash git clone https://gitlab.eurecom.fr/mosaic5g/flexric flexric cd flexric/ -git checkout 29af8408afad3da36824bd7912562202023ae924 +git checkout 34d6810f9ab435ac3a4023d5e1917669e24341da ``` ### 2.2.2 Build FlexRIC @@ -113,27 +115,47 @@ sudo make install By default the service model libraries will be installed in the path `/usr/local/lib/flexric` while the configuration file in `/usr/local/etc/flexric`. -#### 2.2.3.1 Available Service Models -1. KPM v02.03 and KPM v03.00 (`xapp_kpm_moni`) -At the moment, the following measurements are supported in OAI RAN: -* "DRB.PdcpSduVolumeDL" -* "DRB.PdcpSduVolumeUL" -* "DRB.RlcSduDelayDl" -* "DRB.UEThpDl" -* "DRB.UEThpUl" -* "RRU.PrbTotDl" -* "RRU.PrbTotUl" +# 3. Service Models available in OAI RAN -2. RC v01.03 -Currently available RC xApps: -* `xapp_kpm_rc` - RAN control function "QoS flow mapping configuration" support, i.e. creating a new DRB. -* `xapp_rc_moni` - aperiodic subscription support for "UE RRC State Change" +## 3.1 O-RAN -3. MAC + RLC + PDCP + GTP (`xapp_gtp_mac_rlc_pdcp_moni`) +We assume that user is familiar with O-RAN WG3 specifications that can be found at https://orandownloadsweb.azurewebsites.net/specifications. + +### 3.1.1 E2SM-KPM + +As mentioned in section [2.1.2 Build OAI with E2 Agent](#212-build-oai-with-e2-agent), we support KPM v2.03/v3.00. Uses ASN.1 encoding. + +Per O-RAN specifications, 5G measurements supported by KPM are specified in 3GPP TS 28.552. + +From 3GPP TS 28.552, we support the following list: + * "DRB.PdcpSduVolumeDL" + * "DRB.PdcpSduVolumeUL" + * "DRB.RlcSduDelayDl" + * "DRB.UEThpDl" + * "DRB.UEThpUl" + * "RRU.PrbTotDl" + * "RRU.PrbTotUl" + +From O-RAN.WG3.E2SM-KPM-version specification, we implemented: + * REPORT Service Style 4 ("Common condition-based, UE-level" - section 7.4.5) - fetch above measurements per each UE that matches common criteria (e.g. S-NSSAI). + +### 3.1.2 E2SM-RC + +We support RC v1.03. Uses ASN.1 encoding. + +From ORAN.WG3.E2SM-RC-v01.03 specification, we implemented: + * REPORT Service Style 4 ("UE Information" - section 7.4.5) - aperiodic subscription for "UE RRC State Change" + * CONTROL Service Style 1 ("Radio Bearer Control" - section 7.6.2) - "QoS flow mapping configuration" (e.g creating a new DRB) + +## 3.2 Custom Service Models + +In addition, we support custom Service Models, such are MAC, RLC, PDCP, and GTP. Use plain encoding. If you are interested in TC and SLICE SMs, please follow the instructions at https://gitlab.eurecom.fr/mosaic5g/flexric. -# 3. Start the process +# 4. Start the process + +At this point, we assume the 5G Core Network is already running in the background. For more information, please follow the tutorial at https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_OAI_CN5G.md. In order to configure E2 agent, please, add the following block in OAI's configuration file: ```bash @@ -142,61 +164,75 @@ e2_agent = { sm_dir = "/usr/local/lib/flexric/" } ``` +* start E2 Node agents -* start the gNB-mono -```bash -cd oai/cmake_targets/ran_build/build -sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --rfsim --sa -E -``` + As per O-RAN.WG3.E2SM-v02.00 specifications, UE ID (section 6.2.2.6) representation in OAI is: + | | gNB-mono | CU | CU-CP | CU-UP | DU | + |:----------------------|:----------------|:----------------|:----------------|:----------------------|:-------------------| + | CHOICE UE ID case | GNB_UE_ID_E2SM | GNB_UE_ID_E2SM | GNB_UE_ID_E2SM | GNB_CU_UP_UE_ID_E2SM | GNB_DU_UE_ID_E2SM | + | AMF UE NGAP ID | amf_ue_ngap_id | amf_ue_ngap_id | amf_ue_ngap_id | | | + | GUAMI | guami | guami | guami | | | + | gNB-CU UE F1AP ID | | rrc_ue_id | | | rrc_ue_id | + | gNB-CU-CP UE E1AP ID | | | rrc_ue_id | rrc_ue_id | | + | RAN UE ID | rrc_ue_id | rrc_ue_id | rrc_ue_id | rrc_ue_id | rrc_ue_id | -* if CU/DU split is used, start the gNB as follows -```bash -sudo ./nr-softmodem -O --rfsim --sa -E -sudo ./nr-softmodem -O --sa -``` + * start the gNB-mono + ```bash + cd oai/cmake_targets/ran_build/build + sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --rfsim --sa -E + ``` -* if CU-CP/CU-UP/DU split is used, start the gNB as follows -```bash -sudo ./nr-softmodem -O --rfsim --sa -E -sudo ./nr-softmodem -O --sa -sudo ./nr-cuup -O --sa -``` + * if CU/DU split is used, start the gNB as follows + ```bash + sudo ./nr-softmodem -O --rfsim --sa -E + sudo ./nr-softmodem -O --sa + ``` + + * if CU-CP/CU-UP/DU split is used, start the gNB as follows + ```bash + sudo ./nr-softmodem -O --rfsim --sa -E + sudo ./nr-softmodem -O --sa + sudo ./nr-cuup -O --sa + ``` * start the nrUE -```bash -cd oai/cmake_targets/ran_build/build -sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --sa --uicc0.imsi 001010000000001 --rfsimulator.serveraddr 127.0.0.1 -``` + ```bash + cd oai/cmake_targets/ran_build/build + sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --sa --uicc0.imsi 001010000000001 --rfsimulator.serveraddr 127.0.0.1 + ``` * start the nearRT-RIC -```bash -cd flexric -./build/examples/ric/nearRT-RIC -``` + ```bash + cd flexric + ./build/examples/ric/nearRT-RIC + ``` -* start the KPM monitor xApp -```bash -cd flexric -./build/examples/xApp/c/monitor/xapp_kpm_moni -``` +* Start different xApps -* start the RC monitor xApp -```bash -cd flexric -./build/examples/xApp/c/monitor/xapp_rc_moni -``` + * start the KPM monitor xApp - measurements stated in [3.1.1 E2SM-KPM](#311-e2sm-kpm) for each UE that matches S-NSSAI common criteria + ```bash + cd flexric + ./build/examples/xApp/c/monitor/xapp_kpm_moni + ``` + Note: we assume that each UE has only 1 DRB; CU-UP does not store the slices, therefore "coarse filtering" is used -* start the RC control xApp -```bash -cd flexric -./build/examples/xApp/c/kpm_rc/xapp_kpm_rc -``` + * start the RC monitor xApp - aperiodic subscription for "UE RRC State Change" + ```bash + cd flexric + ./build/examples/xApp/c/monitor/xapp_rc_moni + ``` -* start the (MAC + RLC + PDCP + GTP) monitor xApp -```bash -cd flexric -./build/examples/xApp/c/monitor/xapp_gtp_mac_rlc_pdcp_moni -``` + * start the RC control xApp - RAN control function "QoS flow mapping configuration" (e.g. creating a new DRB) + ```bash + cd flexric + ./build/examples/xApp/c/kpm_rc/xapp_kpm_rc + ``` + + * start the (MAC + RLC + PDCP + GTP) monitor xApp + ```bash + cd flexric + ./build/examples/xApp/c/monitor/xapp_gtp_mac_rlc_pdcp_moni + ``` The latency that you observe in your monitor xApp is the latency from the E2 Agent to the nearRT-RIC and xApp. Therefore, FlexRIC is well suited for use cases with ultra low-latency requirements. Additionally, all the data received in the `xapp_gtp_mac_rlc_pdcp_moni` xApp is also written to /tmp/xapp_db in case that offline data processing is wanted (e.g., Machine Learning/Artificial Intelligence applications). You can browse the data using e.g., sqlitebrowser. diff --git a/openair2/E2AP/flexric b/openair2/E2AP/flexric index 7c424e882a..34d6810f9a 160000 --- a/openair2/E2AP/flexric +++ b/openair2/E2AP/flexric @@ -1 +1 @@ -Subproject commit 7c424e882adae6ad72a24470d8c3e731189062c0 +Subproject commit 34d6810f9ab435ac3a4023d5e1917669e24341da