diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index 84d887f8f9..85f2def720 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -26,6 +26,7 @@ #include #include "common/5g_platform_types.h" #include "common/platform_constants.h" +#include "common/utils/ds/byte_array.h" //-------------------------------------------------------------------------------------------// // Defines to access message fields. @@ -452,6 +453,13 @@ typedef struct cu_to_du_rrc_information_s { uint32_t handoverPreparationInfo_length; }cu_to_du_rrc_information_t; +typedef struct f1ap_cu_to_du_rrc_info_s { + byte_array_t *cg_configinfo; + byte_array_t *ue_cap; + byte_array_t *meas_config; + byte_array_t *ho_prep_info; +} f1ap_cu_to_du_rrc_info_t; + typedef struct du_to_cu_rrc_information_s { uint8_t * cellGroupConfig; uint32_t cellGroupConfig_length; @@ -477,6 +485,97 @@ typedef enum TransmActionInd_e { TransmActionInd_RESTART, } TransmActionInd_t; +typedef struct f1ap_srb_to_setup_t { + int id; +} f1ap_srb_to_setup_t; + +/// 9.3.1.52 Packet Error Rate +typedef struct f1ap_per_t { + uint8_t scalar; + uint8_t exponent; +} f1ap_per_t; + +// 9.3.1.49 Non-Dynamic 5QI Descriptor +typedef struct f1ap_nondynamic_5qi_t { + int fiveQI; +} f1ap_nondynamic_5qi_t; + +// 9.3.1.47 Dynamic 5QI Descriptor +typedef struct f1ap_dynamic_5qi_t { + int prio; /// QoS Priority Level + int pdb; /// Packet Delay Budget + f1ap_per_t per; /// Packet Error Rate + bool *delay_critical; /// Delay Critical + int *avg_win; /// Averaging Window +} f1ap_dynamic_5qi_t; + +typedef struct f1ap_arp_t { + uint16_t prio; + preemption_capability_t preempt_cap; + preemption_vulnerability_t preempt_vuln; +} f1ap_arp_t; + +// 9.3.1.45 QoS Flow Level QoS Parameters +typedef struct f1ap_qos_flow_param_t { + fiveQI_t qos_type; + union { + f1ap_nondynamic_5qi_t nondyn; + f1ap_dynamic_5qi_t dyn; + }; + f1ap_arp_t arp; +} f1ap_qos_flow_param_t; + +// in 9.2.2.1 Flows Mapped to DRB Item +typedef struct f1ap_drb_flows_mapped_t { + int qfi; + f1ap_qos_flow_param_t param; +} f1ap_drb_flows_mapped_t; + +/// in 9.2.2.1 DRB Info in UE Context Setup Request +typedef struct f1ap_drb_info_nr_t { + f1ap_qos_flow_param_t drb_qos; + nssai_t nssai; + int flows_len; + f1ap_drb_flows_mapped_t *flows; +} f1ap_drb_info_nr_t; + +typedef enum { F1AP_PDCP_SN_12B, F1AP_PDCP_SN_18B } f1ap_pdcp_sn_len_t; +/// in 9.2.2.1 DRB to Be Setup Item IEs +typedef struct f1ap_drb_to_setup_t { + int id; + enum { F1AP_QOS_CHOICE_EUTRAN, F1AP_QOS_CHOICE_NR } qos_choice; + union { + // eutran not implemented + f1ap_drb_info_nr_t nr; + }; + int up_ul_tnl_len; + f1ap_up_tnl_t up_ul_tnl[2]; + f1ap_rlc_mode_t rlc_mode; + f1ap_pdcp_sn_len_t *dl_pdcp_sn_len; // in setup mandatory, in setupmod optional + f1ap_pdcp_sn_len_t *ul_pdcp_sn_len; +} f1ap_drb_to_setup_t; + +typedef struct f1ap_ue_context_setup_req_s { + uint32_t gNB_CU_ue_id; + uint32_t *gNB_DU_ue_id; + + plmn_id_t plmn; + uint64_t nr_cellid; + uint8_t servCellIndex; + + f1ap_cu_to_du_rrc_info_t cu_to_du_rrc_info; + + int srbs_len; + f1ap_srb_to_setup_t *srbs; + + int drbs_len; + f1ap_drb_to_setup_t *drbs; + + byte_array_t *rrc_container; + + uint64_t *gnb_du_ue_agg_mbr_ul; // C-ifDRBSetup +} f1ap_ue_context_setup_req_t; + typedef struct f1ap_ue_context_setup_s { uint32_t gNB_CU_ue_id; uint32_t gNB_DU_ue_id; diff --git a/openair2/F1AP/lib/CMakeLists.txt b/openair2/F1AP/lib/CMakeLists.txt index 0c6137dc30..978f69fb29 100644 --- a/openair2/F1AP/lib/CMakeLists.txt +++ b/openair2/F1AP/lib/CMakeLists.txt @@ -2,8 +2,9 @@ add_library(f1ap_lib OBJECT f1ap_lib_common.c f1ap_rrc_message_transfer.c f1ap_interface_management.c + f1ap_ue_context.c ) -target_link_libraries(f1ap_lib PRIVATE asn1_f1ap) +target_link_libraries(f1ap_lib PRIVATE asn1_f1ap ds) target_include_directories(f1ap_lib PUBLIC ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/openair2/F1AP/lib/f1ap_lib_includes.h b/openair2/F1AP/lib/f1ap_lib_includes.h index 267e7243e7..e34ec0598d 100644 --- a/openair2/F1AP/lib/f1ap_lib_includes.h +++ b/openair2/F1AP/lib/f1ap_lib_includes.h @@ -41,5 +41,10 @@ #include "F1AP_SibtypetobeupdatedListItem.h" #include "F1AP_UnsuccessfulOutcome.h" #include "F1AP_UE-associatedLogicalF1-ConnectionListRes.h" +#include "F1AP_ULUPTNLInformation-ToBeSetup-Item.h" +#include "F1AP_GTPTunnel.h" +#include "F1AP_Flows-Mapped-To-DRB-Item.h" +#include "F1AP_NonDynamic5QIDescriptor.h" +#include "F1AP_Dynamic5QIDescriptor.h" #endif /* F1AP_LIB_INCLUDES_H_ */ diff --git a/openair2/F1AP/lib/f1ap_ue_context.c b/openair2/F1AP/lib/f1ap_ue_context.c new file mode 100644 index 0000000000..572ad3ce6a --- /dev/null +++ b/openair2/F1AP/lib/f1ap_ue_context.c @@ -0,0 +1,876 @@ +/* + * 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 "f1ap_ue_context.h" + +#include "f1ap_lib_common.h" +#include "f1ap_lib_includes.h" +#include "f1ap_messages_types.h" + +#include "common/utils/assertions.h" +#include "openair3/UTILS/conversions.h" +#include "common/utils/oai_asn1.h" +#include "common/utils/utils.h" +#include "common/utils/ds/byte_array.h" + +static F1AP_CUtoDURRCInformation_t encode_cu_to_du_rrc_info(const f1ap_cu_to_du_rrc_info_t *cu2du) +{ + F1AP_CUtoDURRCInformation_t enc = {0}; + + /* optional: cG_ConfigInfo */ + if (cu2du->cg_configinfo) { + asn1cCalloc(enc.cG_ConfigInfo, cG_ConfigInfo); + const byte_array_t *ba = cu2du->cg_configinfo; + OCTET_STRING_fromBuf(cG_ConfigInfo, (const char *)ba->buf, ba->len); + } + + /* optional: uE_CapabilityRAT_ContainerList */ + if (cu2du->ue_cap) { + asn1cCalloc(enc.uE_CapabilityRAT_ContainerList, uE_CapabilityRAT_ContainerList); + const byte_array_t *ba = cu2du->ue_cap; + OCTET_STRING_fromBuf(uE_CapabilityRAT_ContainerList, (const char *)ba->buf, ba->len); + } + + /* optional: measConfig */ + if (cu2du->meas_config) { + asn1cCalloc(enc.measConfig, measConfig); + const byte_array_t *ba = cu2du->meas_config; + OCTET_STRING_fromBuf(measConfig, (const char *)ba->buf, ba->len); + } + + /* optional: HandoverPreparationInformation */ + if (cu2du->ho_prep_info) { + F1AP_ProtocolExtensionContainer_10696P60_t *p = calloc_or_fail(1, sizeof(*p)); + enc.iE_Extensions = (struct F1AP_ProtocolExtensionContainer *)p; + asn1cSequenceAdd(p->list, F1AP_CUtoDURRCInformation_ExtIEs_t, ie_ext); + ie_ext->id = F1AP_ProtocolIE_ID_id_HandoverPreparationInformation; + ie_ext->criticality = F1AP_Criticality_ignore; + ie_ext->extensionValue.present = F1AP_CUtoDURRCInformation_ExtIEs__extensionValue_PR_HandoverPreparationInformation; + const byte_array_t *ba = cu2du->ho_prep_info; + F1AP_HandoverPreparationInformation_t *hpi = &ie_ext->extensionValue.choice.HandoverPreparationInformation; + OCTET_STRING_fromBuf(hpi, (const char *)ba->buf, ba->len); + } + return enc; +} + +static bool decode_cu_to_du_rrc_info(f1ap_cu_to_du_rrc_info_t *dec, const F1AP_CUtoDURRCInformation_t *cu2du) +{ + if (cu2du->cG_ConfigInfo) { + dec->cg_configinfo = calloc_or_fail(1, sizeof(*dec->cg_configinfo)); + const F1AP_CG_ConfigInfo_t *cgci = cu2du->cG_ConfigInfo; + *dec->cg_configinfo = create_byte_array(cgci->size, (uint8_t*)cgci->buf); + } + if (cu2du->uE_CapabilityRAT_ContainerList) { + dec->ue_cap = calloc_or_fail(1, sizeof(*dec->ue_cap)); + const F1AP_UE_CapabilityRAT_ContainerList_t *l = cu2du->uE_CapabilityRAT_ContainerList; + *dec->ue_cap = create_byte_array(l->size, (uint8_t*)l->buf); + } + if (cu2du->measConfig) { + dec->meas_config = calloc_or_fail(1, sizeof(*dec->meas_config)); + const F1AP_MeasConfig_t *mc = cu2du->measConfig; + *dec->meas_config = create_byte_array(mc->size, (uint8_t*)mc->buf); + } + if (cu2du->iE_Extensions) { + const F1AP_ProtocolExtensionContainer_10696P60_t *ext = (const F1AP_ProtocolExtensionContainer_10696P60_t *)cu2du->iE_Extensions; + for (int i = 0; i < ext->list.count; ++i) { + const F1AP_CUtoDURRCInformation_ExtIEs_t *cu2du_info_ext = ext->list.array[i]; + switch (cu2du_info_ext->id) { + case F1AP_ProtocolIE_ID_id_HandoverPreparationInformation: + dec->ho_prep_info = calloc_or_fail(1, sizeof(*dec->ho_prep_info)); + const F1AP_HandoverPreparationInformation_t *hopi = &cu2du_info_ext->extensionValue.choice.HandoverPreparationInformation; + *dec->ho_prep_info = create_byte_array(hopi->size, (uint8_t*) hopi->buf); + break; + default: + PRINT_ERROR("received unsupported F1AP CUtoDURRCInfo extension container %ld\n", cu2du_info_ext->id); + } + } + } + return true; +} + +static f1ap_cu_to_du_rrc_info_t cp_cu_to_du_rrc_info(const f1ap_cu_to_du_rrc_info_t *src) +{ + f1ap_cu_to_du_rrc_info_t dst = {0}; + CP_OPT_BYTE_ARRAY(dst.cg_configinfo, src->cg_configinfo); + CP_OPT_BYTE_ARRAY(dst.ue_cap, src->ue_cap); + CP_OPT_BYTE_ARRAY(dst.meas_config, src->meas_config); + CP_OPT_BYTE_ARRAY(dst.ho_prep_info, src->ho_prep_info); + return dst; +} + +static bool eq_ba(const byte_array_t a, const byte_array_t b) +{ + return eq_byte_array(&a, &b); +} + +static bool eq_cu_to_du_rrc_info(const f1ap_cu_to_du_rrc_info_t *a, const f1ap_cu_to_du_rrc_info_t *b) +{ + _F1_EQ_CHECK_OPTIONAL_IE(a, b, cg_configinfo, eq_ba); + _F1_EQ_CHECK_OPTIONAL_IE(a, b, ue_cap, eq_ba); + _F1_EQ_CHECK_OPTIONAL_IE(a, b, meas_config, eq_ba); + _F1_EQ_CHECK_OPTIONAL_IE(a, b, ho_prep_info, eq_ba); + return true; +} + +static void free_cu_to_du_rrc_info(f1ap_cu_to_du_rrc_info_t *cu2du) +{ + FREE_OPT_BYTE_ARRAY(cu2du->cg_configinfo); + FREE_OPT_BYTE_ARRAY(cu2du->ue_cap); + FREE_OPT_BYTE_ARRAY(cu2du->meas_config); + FREE_OPT_BYTE_ARRAY(cu2du->ho_prep_info); +} + +/* \brief Encode SRB-ToBeSetup List, for UE Context Setup Request, from + * f1ap_srb_to_setup_t. */ +static F1AP_SRBs_ToBeSetup_List_t encode_srbs_to_setup(int n, const f1ap_srb_to_setup_t *srbs) +{ + F1AP_SRBs_ToBeSetup_List_t list = {0}; + for (int i = 0; i < n; ++i) { + asn1cSequenceAdd(list, F1AP_SRBs_ToBeSetup_ItemIEs_t, itie); + itie->id = F1AP_ProtocolIE_ID_id_SRBs_ToBeSetup_Item; + itie->criticality = F1AP_Criticality_reject; + itie->value.present = F1AP_SRBs_ToBeSetup_ItemIEs__value_PR_SRBs_ToBeSetup_Item; + itie->value.choice.SRBs_ToBeSetup_Item.sRBID = srbs[i].id; + } + return list; +} + +/* \brief Decode SRB-ToBeSetup List, from UE Context Setup Request, into + * f1ap_srb_to_setup_t. */ +static bool decode_srbs_to_setup(const F1AP_SRBs_ToBeSetup_List_t *f1ap, int *n, f1ap_srb_to_setup_t **out) +{ + DevAssert(out != NULL); + *out = NULL; + int count = *n = f1ap->list.count; + if (count == 0) + return true; + + f1ap_srb_to_setup_t *srbs = *out = calloc_or_fail(count, sizeof(*srbs)); + for (int i = 0; i < count; ++i) { + const F1AP_SRBs_ToBeSetup_ItemIEs_t *itie = (F1AP_SRBs_ToBeSetup_ItemIEs_t *)f1ap->list.array[i]; + _F1_EQ_CHECK_LONG(itie->id, F1AP_ProtocolIE_ID_id_SRBs_ToBeSetup_Item); + _F1_EQ_CHECK_INT(itie->value.present, F1AP_SRBs_ToBeSetup_ItemIEs__value_PR_SRBs_ToBeSetup_Item); + srbs[i].id = itie->value.choice.SRBs_ToBeSetup_Item.sRBID; + } + + return true; +} + +static f1ap_srb_to_setup_t cp_srb_to_setup(const f1ap_srb_to_setup_t *orig) +{ + f1ap_srb_to_setup_t cp = { + .id = orig->id, + }; + return cp; +} + +static bool eq_srb_to_setup(const f1ap_srb_to_setup_t *a, const f1ap_srb_to_setup_t *b) +{ + _F1_EQ_CHECK_INT(a->id, b->id); + return true; +} + +static void free_srb_to_setup(f1ap_srb_to_setup_t *srbs) +{ + // nothing to free +} + +static F1AP_QoSFlowLevelQoSParameters_t encode_qos_flow_param(const f1ap_qos_flow_param_t *p) +{ + F1AP_QoSFlowLevelQoSParameters_t f1ap = {0}; + + if (p->qos_type == NON_DYNAMIC) { + f1ap.qoS_Characteristics.present = F1AP_QoS_Characteristics_PR_non_Dynamic_5QI; + asn1cCalloc(f1ap.qoS_Characteristics.choice.non_Dynamic_5QI, nd); + nd->fiveQI = p->nondyn.fiveQI; + } else { + DevAssert(p->qos_type == DYNAMIC); + f1ap.qoS_Characteristics.present = F1AP_QoS_Characteristics_PR_dynamic_5QI; + asn1cCalloc(f1ap.qoS_Characteristics.choice.dynamic_5QI, d); + + d->qoSPriorityLevel = p->dyn.prio; + d->packetDelayBudget = p->dyn.pdb; + d->packetErrorRate.pER_Scalar = p->dyn.per.scalar; + d->packetErrorRate.pER_Exponent = p->dyn.per.exponent; + + if (p->dyn.delay_critical) { + e_F1AP_Dynamic5QIDescriptor__delayCritical v = *p->dyn.delay_critical + ? F1AP_Dynamic5QIDescriptor__delayCritical_delay_critical + : F1AP_Dynamic5QIDescriptor__delayCritical_non_delay_critical; + asn1cCallocOne(d->delayCritical, v); + } + + if (p->dyn.avg_win) + asn1cCallocOne(d->averagingWindow, *p->dyn.avg_win); + } + + F1AP_NGRANAllocationAndRetentionPriority_t *arp = &f1ap.nGRANallocationRetentionPriority; + arp->priorityLevel = p->arp.prio; + arp->pre_emptionCapability = p->arp.preempt_cap; + arp->pre_emptionVulnerability = p->arp.preempt_vuln; + return f1ap; +} + +static bool decode_qos_flow_param(const F1AP_QoSFlowLevelQoSParameters_t *f1ap, f1ap_qos_flow_param_t *out) +{ + if (f1ap->qoS_Characteristics.present == F1AP_QoS_Characteristics_PR_non_Dynamic_5QI) { + out->qos_type = NON_DYNAMIC; + const F1AP_NonDynamic5QIDescriptor_t *nondyn = f1ap->qoS_Characteristics.choice.non_Dynamic_5QI; + out->nondyn.fiveQI = nondyn->fiveQI; + } else { + _F1_EQ_CHECK_INT(f1ap->qoS_Characteristics.present, F1AP_QoS_Characteristics_PR_dynamic_5QI); + const F1AP_Dynamic5QIDescriptor_t *d = f1ap->qoS_Characteristics.choice.dynamic_5QI; + out->qos_type = DYNAMIC; + out->dyn.prio = d->qoSPriorityLevel; + out->dyn.pdb = d->packetDelayBudget; + out->dyn.per.scalar = d->packetErrorRate.pER_Scalar; + out->dyn.per.exponent = d->packetErrorRate.pER_Exponent; + + if (d->delayCritical) { + bool v = *d->delayCritical == F1AP_Dynamic5QIDescriptor__delayCritical_delay_critical; + _F1_MALLOC(out->dyn.delay_critical, v); + } + + if (d->averagingWindow) + _F1_MALLOC(out->dyn.avg_win, *d->averagingWindow); + } + + const F1AP_NGRANAllocationAndRetentionPriority_t *arp = &f1ap->nGRANallocationRetentionPriority; + out->arp.prio = arp->priorityLevel; + out->arp.preempt_cap = arp->pre_emptionCapability; + out->arp.preempt_vuln = arp->pre_emptionVulnerability; + return true; +} + +static f1ap_qos_flow_param_t cp_qos_flow_param(const f1ap_qos_flow_param_t *orig) +{ + f1ap_qos_flow_param_t cp = { + .qos_type = orig->qos_type, + // dyn/nondyn below + .arp = orig->arp, + }; + if (cp.qos_type == NON_DYNAMIC) { + cp.nondyn.fiveQI = orig->nondyn.fiveQI; + } else { + DevAssert(cp.qos_type == DYNAMIC); + f1ap_dynamic_5qi_t *cpdyn = &cp.dyn; + const f1ap_dynamic_5qi_t *odyn = &orig->dyn; + cpdyn->prio = odyn->prio; + cpdyn->pdb = odyn->pdb; + cpdyn->per.scalar = odyn->per.scalar; + cpdyn->per.exponent = odyn->per.exponent; + if (odyn->delay_critical) + _F1_MALLOC(cpdyn->delay_critical, *odyn->delay_critical); + if (odyn->avg_win) + _F1_MALLOC(cpdyn->avg_win, *odyn->avg_win); + } + return cp; +} + +static bool eq_qos_flow_param(const f1ap_qos_flow_param_t *a, const f1ap_qos_flow_param_t *b) +{ + _F1_EQ_CHECK_INT(a->qos_type, b->qos_type); + if (a->qos_type == NON_DYNAMIC) { + _F1_EQ_CHECK_INT(a->nondyn.fiveQI, b->nondyn.fiveQI); + } else { + DevAssert(a->qos_type == DYNAMIC); + DevAssert(b->qos_type == DYNAMIC); + const f1ap_dynamic_5qi_t *adyn = &a->dyn, *bdyn = &b->dyn; + _F1_EQ_CHECK_INT(adyn->prio, bdyn->prio); + _F1_EQ_CHECK_INT(adyn->pdb, bdyn->pdb); + _F1_EQ_CHECK_INT(adyn->per.scalar, bdyn->per.scalar); + _F1_EQ_CHECK_INT(adyn->per.exponent, bdyn->per.exponent); + _F1_EQ_CHECK_OPTIONAL_IE(adyn, bdyn, delay_critical, _F1_EQ_CHECK_INT); + _F1_EQ_CHECK_OPTIONAL_IE(adyn, bdyn, avg_win, _F1_EQ_CHECK_INT); + } + _F1_EQ_CHECK_INT(a->arp.prio, b->arp.prio); + _F1_EQ_CHECK_INT(a->arp.preempt_cap, b->arp.preempt_cap); + _F1_EQ_CHECK_INT(a->arp.preempt_vuln, b->arp.preempt_vuln); + return true; +} + +static void free_qos_flow_param(f1ap_qos_flow_param_t *p) +{ + if (p->qos_type == NON_DYNAMIC) { + // nothing to free + } else { + DevAssert(p->qos_type == DYNAMIC); + free(p->dyn.delay_critical); + free(p->dyn.avg_win); + } + // arp: nothing to free +} + +static F1AP_DRB_Information_t encode_drb_info_nr(const f1ap_drb_info_nr_t *drb) +{ + F1AP_DRB_Information_t f1ap = {0}; + + f1ap.dRB_QoS = encode_qos_flow_param(&drb->drb_qos); + f1ap.sNSSAI = encode_nssai(&drb->nssai); + + for (int i = 0; i < drb->flows_len; ++i) { + asn1cSequenceAdd(f1ap.flows_Mapped_To_DRB_List.list, F1AP_Flows_Mapped_To_DRB_Item_t, it); + it->qoSFlowIdentifier = drb->flows[i].qfi; + it->qoSFlowLevelQoSParameters = encode_qos_flow_param(&drb->flows[i].param); + } + + return f1ap; +} + +static bool decode_drb_info_nr(const F1AP_DRB_Information_t *f1ap, f1ap_drb_info_nr_t *out) +{ + _F1_CHECK_EXP(decode_qos_flow_param(&f1ap->dRB_QoS, &out->drb_qos)); + out->nssai = decode_nssai(&f1ap->sNSSAI); + out->flows_len = f1ap->flows_Mapped_To_DRB_List.list.count; + out->flows = calloc_or_fail(out->flows_len, sizeof(*out->flows)); + for (int i = 0; i < out->flows_len; ++i) { + const F1AP_Flows_Mapped_To_DRB_Item_t *it = f1ap->flows_Mapped_To_DRB_List.list.array[i]; + out->flows[i].qfi = it->qoSFlowIdentifier; + _F1_CHECK_EXP(decode_qos_flow_param(&it->qoSFlowLevelQoSParameters, &out->flows[i].param)); + } + return true; +} + +static f1ap_drb_info_nr_t cp_drb_info_nr(const f1ap_drb_info_nr_t *orig) +{ + f1ap_drb_info_nr_t cp = { + .drb_qos = cp_qos_flow_param(&orig->drb_qos), + .nssai = orig->nssai, + .flows_len = orig->flows_len, + // flows below + }; + cp.flows = calloc_or_fail(cp.flows_len, sizeof(*cp.flows)); + for (int i = 0; i < cp.flows_len; ++i) { + cp.flows[i].qfi = orig->flows[i].qfi; + cp.flows[i].param = cp_qos_flow_param(&orig->flows[i].param); + } + return cp; +} + +static bool eq_drb_info_nr(const f1ap_drb_info_nr_t *a, const f1ap_drb_info_nr_t *b) +{ + _F1_CHECK_EXP(eq_qos_flow_param(&a->drb_qos, &b->drb_qos)); + _F1_EQ_CHECK_INT(a->nssai.sst, b->nssai.sst); + _F1_EQ_CHECK_INT(a->nssai.sd, b->nssai.sd); + _F1_EQ_CHECK_INT(a->flows_len, b->flows_len); + for (int i = 0; i < a->flows_len; ++i) { + _F1_EQ_CHECK_INT(a->flows[i].qfi, b->flows[i].qfi); + _F1_CHECK_EXP(eq_qos_flow_param(&a->flows[i].param, &b->flows[i].param)); + } + return true; +} + +static void free_drb_info_nr(f1ap_drb_info_nr_t *p) +{ + free_qos_flow_param(&p->drb_qos); + // nssai: nothing + for (int i = 0; i < p->flows_len; ++i) + free_qos_flow_param(&p->flows[i].param); + free(p->flows); +} + +static F1AP_RLCMode_t rlc_mode_to_asn1(f1ap_rlc_mode_t mode) +{ + switch (mode) { + case F1AP_RLC_MODE_AM: + return F1AP_RLCMode_rlc_am; + case F1AP_RLC_MODE_UM_BIDIR: + return F1AP_RLCMode_rlc_um_bidirectional; + default: + break; + } + AssertFatal(false, "unsupported RLC Mode %d received\n", mode); + return F1AP_RLCMode_rlc_am; // won't be reached +} + +static bool rlc_mode_from_asn1(F1AP_RLCMode_t mode, f1ap_rlc_mode_t *out) +{ + switch (mode) { + case F1AP_RLCMode_rlc_am: + *out = F1AP_RLC_MODE_AM; + return true; + case F1AP_RLCMode_rlc_um_bidirectional: + *out = F1AP_RLC_MODE_UM_BIDIR; + return true; + default: + PRINT_ERROR("unsupported RLC Mode %ld received: setting AM\n", mode); + break; + } + return false; +} + +static F1AP_UPTransportLayerInformation_t encode_up_tnl(const f1ap_up_tnl_t *tnl) +{ + F1AP_UPTransportLayerInformation_t tnl_info = { .present = F1AP_UPTransportLayerInformation_PR_gTPTunnel, }; + asn1cCalloc(tnl_info.choice.gTPTunnel, gtp_tnl); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(tnl->tl_address, >p_tnl->transportLayerAddress); + INT32_TO_OCTET_STRING(tnl->teid, >p_tnl->gTP_TEID); + return tnl_info; +} + +static bool decode_up_tnl(const F1AP_UPTransportLayerInformation_t *tnl_info, f1ap_up_tnl_t *out) +{ + _F1_EQ_CHECK_INT(tnl_info->present, F1AP_UPTransportLayerInformation_PR_gTPTunnel); + const F1AP_GTPTunnel_t *gtp_tnl = tnl_info->choice.gTPTunnel; + BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(>p_tnl->transportLayerAddress, out->tl_address); + OCTET_STRING_TO_UINT32(>p_tnl->gTP_TEID, out->teid); + return true; +} + +static f1ap_up_tnl_t cp_up_tnl(const f1ap_up_tnl_t *orig) +{ + f1ap_up_tnl_t cp = { .teid = orig->teid, }; + memcpy(&cp.tl_address, &orig->tl_address, sizeof(orig->tl_address)); + return cp; +} + +static bool eq_up_tnl(const f1ap_up_tnl_t *a, const f1ap_up_tnl_t *b) +{ + if (memcmp(&a->tl_address, &b->tl_address, sizeof(a->tl_address)) != 0) { + PRINT_ERROR("tl_address mismatch\n"); + return false; + } + _F1_EQ_CHECK_INT(a->teid, b->teid); + return true; +} + +static void free_up_tnl(const f1ap_up_tnl_t *tnl) +{ + // nothing to free +} + +/* Encode a DRBs_ToSetup list for UE Context Setup Request, from f1ap_drb_to_setup_t. */ +static F1AP_DRBs_ToBeSetup_List_t encode_drbs_to_setup(int n, const f1ap_drb_to_setup_t *drbs) +{ + F1AP_DRBs_ToBeSetup_List_t list = {0}; + for (int i = 0; i < n; ++i) { + const f1ap_drb_to_setup_t *drb = &drbs[i]; + asn1cSequenceAdd(list, F1AP_DRBs_ToBeSetup_ItemIEs_t, itie); + itie->id = F1AP_ProtocolIE_ID_id_DRBs_ToBeSetup_Item; + itie->criticality = F1AP_Criticality_reject; + itie->value.present = F1AP_DRBs_ToBeSetup_ItemIEs__value_PR_DRBs_ToBeSetup_Item; + + F1AP_DRBs_ToBeSetup_Item_t *it = &itie->value.choice.DRBs_ToBeSetup_Item; + it->dRBID = drb->id; + AssertFatal(drb->qos_choice == F1AP_QOS_CHOICE_NR, "only NR QoS choice implemented\n"); + it->qoSInformation.present = F1AP_QoSInformation_PR_choice_extension; + F1AP_QoSInformation_ExtIEs_t *qos_ext_ie = calloc_or_fail(1, sizeof(*qos_ext_ie)); + it->qoSInformation.choice.choice_extension = (struct F1AP_ProtocolIE_SingleContainer *)qos_ext_ie; + qos_ext_ie->id = F1AP_ProtocolIE_ID_id_DRB_Information; + qos_ext_ie->criticality = F1AP_Criticality_reject; + qos_ext_ie->value.present = F1AP_QoSInformation_ExtIEs__value_PR_DRB_Information; + qos_ext_ie->value.choice.DRB_Information = encode_drb_info_nr(&drb->nr); + + /* 12.1.3 uLUPTNLInformation_ToBeSetup_List */ + for (int j = 0; j < drb->up_ul_tnl_len; j++) { + DevAssert(drb->up_ul_tnl[j].teid > 0); + asn1cSequenceAdd(it->uLUPTNLInformation_ToBeSetup_List.list, F1AP_ULUPTNLInformation_ToBeSetup_Item_t, tnl_it); + tnl_it->uLUPTNLInformation = encode_up_tnl(&drb->up_ul_tnl[j]); + } + + it->rLCMode = rlc_mode_to_asn1(drb->rlc_mode); + + F1AP_ProtocolExtensionContainer_10696P82_t *ext = calloc_or_fail(1, sizeof(*ext)); + it->iE_Extensions = (struct F1AP_ProtocolExtensionContainer *)ext; + asn1cSequenceAdd(ext->list, F1AP_DRBs_ToBeSetup_ItemExtIEs_t, ext_ie); + ext_ie->id = F1AP_ProtocolIE_ID_id_DLPDCPSNLength; + ext_ie->criticality = F1AP_Criticality_ignore; + ext_ie->extensionValue.present = F1AP_DRBs_ToBeSetup_ItemExtIEs__extensionValue_PR_PDCPSNLength; + AssertFatal(drb->dl_pdcp_sn_len != NULL, "dl_pdcp_sn_len required for setup\n"); + ext_ie->extensionValue.choice.PDCPSNLength = *drb->dl_pdcp_sn_len; + + if (drb->ul_pdcp_sn_len) { + asn1cSequenceAdd(ext->list, F1AP_DRBs_ToBeSetup_ItemExtIEs_t, ext_ie); + ext_ie->id = F1AP_ProtocolIE_ID_id_ULPDCPSNLength; + ext_ie->criticality = F1AP_Criticality_ignore; + ext_ie->extensionValue.present = F1AP_DRBs_ToBeSetup_ItemExtIEs__extensionValue_PR_PDCPSNLength_1; + ext_ie->extensionValue.choice.PDCPSNLength_1 = *drb->ul_pdcp_sn_len; + } + } + return list; +} + +/* \brief Decode DRBS-ToBeSetup list from UE Context Setup request, into + * f1ap_drb_to_setup_t. */ +static bool decode_drbs_to_setup(const F1AP_DRBs_ToBeSetup_List_t *f1ap, int *n, f1ap_drb_to_setup_t **out) +{ + DevAssert(out != NULL); + *out = NULL; + int count = *n = f1ap->list.count; + if (count == 0) + return true; + + f1ap_drb_to_setup_t *drbs = *out = calloc_or_fail(count, sizeof(*drbs)); + for (int i = 0; i < count; ++i) { + const F1AP_DRBs_ToBeSetup_ItemIEs_t *itie = (const F1AP_DRBs_ToBeSetup_ItemIEs_t *)f1ap->list.array[i]; + _F1_EQ_CHECK_LONG(itie->id, F1AP_ProtocolIE_ID_id_DRBs_ToBeSetup_Item); + _F1_EQ_CHECK_INT(itie->value.present, F1AP_DRBs_ToBeSetup_ItemIEs__value_PR_DRBs_ToBeSetup_Item); + const F1AP_DRBs_ToBeSetup_Item_t *it = &itie->value.choice.DRBs_ToBeSetup_Item; + + f1ap_drb_to_setup_t *drb = &drbs[i]; + drb->id = it->dRBID; + + _F1_EQ_CHECK_INT(it->qoSInformation.present, F1AP_QoSInformation_PR_choice_extension); + const F1AP_QoSInformation_ExtIEs_t *qos_ext = (F1AP_QoSInformation_ExtIEs_t *)it->qoSInformation.choice.choice_extension; + _F1_EQ_CHECK_LONG(qos_ext->id, F1AP_ProtocolIE_ID_id_DRB_Information); + _F1_EQ_CHECK_INT(qos_ext->value.present, F1AP_QoSInformation_ExtIEs__value_PR_DRB_Information); + drb->qos_choice = F1AP_QOS_CHOICE_NR; + _F1_CHECK_EXP(decode_drb_info_nr(&qos_ext->value.choice.DRB_Information, &drb->nr)); + + drb->up_ul_tnl_len = it->uLUPTNLInformation_ToBeSetup_List.list.count; + _F1_EQ_CHECK_GENERIC(drb->up_ul_tnl_len > 0 && drb->up_ul_tnl_len <= 2, "%d", drb->up_ul_tnl_len, 1); + for (int j = 0; j < drb->up_ul_tnl_len; ++j) { + const F1AP_ULUPTNLInformation_ToBeSetup_Item_t *it_tnl = it->uLUPTNLInformation_ToBeSetup_List.list.array[j]; + _F1_CHECK_EXP(decode_up_tnl(&it_tnl->uLUPTNLInformation, &drb->up_ul_tnl[j])); + } + + _F1_CHECK_EXP(rlc_mode_from_asn1(it->rLCMode, &drb->rlc_mode)); + + _F1_CHECK_EXP(it->iE_Extensions); // PDCP SN length is under extension, is mandatory + const F1AP_ProtocolExtensionContainer_10696P82_t *ext = (const F1AP_ProtocolExtensionContainer_10696P82_t *)it->iE_Extensions; + const F1AP_DRBs_ToBeSetup_ItemExtIEs_t *ie; + F1AP_LIB_FIND_IE(F1AP_DRBs_ToBeSetup_ItemExtIEs_t, ie, &ext->list, F1AP_ProtocolIE_ID_id_DLPDCPSNLength, true); + + for (int j = 0; j < ext->list.count; ++j) { + ie = ext->list.array[j]; + AssertError(ie != NULL, return false, "ext->list.array[j] is NULL"); + switch (ie->id) { + case F1AP_ProtocolIE_ID_id_DLPDCPSNLength: + _F1_EQ_CHECK_INT(ie->extensionValue.present, F1AP_DRBs_ToBeSetup_ItemExtIEs__extensionValue_PR_PDCPSNLength); + _F1_MALLOC(drb->dl_pdcp_sn_len, ie->extensionValue.choice.PDCPSNLength); + break; + case F1AP_ProtocolIE_ID_id_ULPDCPSNLength: + _F1_EQ_CHECK_INT(ie->extensionValue.present, F1AP_DRBs_ToBeSetup_ItemExtIEs__extensionValue_PR_PDCPSNLength_1); + _F1_MALLOC(drb->ul_pdcp_sn_len, ie->extensionValue.choice.PDCPSNLength_1); + break; + default: + PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); + break; + } + } + } + + return true; +} + +static f1ap_drb_to_setup_t cp_drb_to_setup(const f1ap_drb_to_setup_t *orig) +{ + f1ap_drb_to_setup_t cp = { + .id = orig->id, + .qos_choice = orig->qos_choice, + // nr below + .up_ul_tnl_len = orig->up_ul_tnl_len, + .up_ul_tnl[0] = cp_up_tnl(&orig->up_ul_tnl[0]), + .up_ul_tnl[1] = cp_up_tnl(&orig->up_ul_tnl[1]), + .rlc_mode = orig->rlc_mode, + // dl_pdcp_sn_len below + // ul_pdcp_sn_len below + }; + AssertFatal(cp.qos_choice == F1AP_QOS_CHOICE_NR, "only NR QoS choice implemented\n"); + if (orig->qos_choice == F1AP_QOS_CHOICE_NR) + cp.nr = cp_drb_info_nr(&orig->nr); + if (orig->dl_pdcp_sn_len) { + _F1_MALLOC(cp.dl_pdcp_sn_len, *orig->dl_pdcp_sn_len); + } + if (orig->ul_pdcp_sn_len) { + _F1_MALLOC(cp.ul_pdcp_sn_len, *orig->ul_pdcp_sn_len); + } + return cp; +} + +static bool eq_drb_to_setup(const f1ap_drb_to_setup_t *a, const f1ap_drb_to_setup_t *b) +{ + _F1_EQ_CHECK_INT(a->id, b->id); + _F1_EQ_CHECK_INT(a->qos_choice, b->qos_choice); + AssertFatal(a->qos_choice == F1AP_QOS_CHOICE_NR, "only NR QoS choice implemented\n"); + if (a->qos_choice == F1AP_QOS_CHOICE_NR) + eq_drb_info_nr(&a->nr, &b->nr); + _F1_EQ_CHECK_INT(a->up_ul_tnl_len, b->up_ul_tnl_len); + for (int i = 0; i < a->up_ul_tnl_len; ++i) { + _F1_CHECK_EXP(eq_up_tnl(&a->up_ul_tnl[i], &b->up_ul_tnl[i])); + } + _F1_EQ_CHECK_INT(a->rlc_mode, b->rlc_mode); + _F1_EQ_CHECK_OPTIONAL_IE(a, b, dl_pdcp_sn_len, _F1_EQ_CHECK_INT); + _F1_EQ_CHECK_OPTIONAL_IE(a, b, ul_pdcp_sn_len, _F1_EQ_CHECK_INT); + return true; +} + +static void free_drb_to_setup(f1ap_drb_to_setup_t *drb) +{ + if (drb->qos_choice == F1AP_QOS_CHOICE_NR) + free_drb_info_nr(&drb->nr); + for (int i = 0; i < drb->up_ul_tnl_len; ++i) + free_up_tnl(&drb->up_ul_tnl[i]); + free(drb->dl_pdcp_sn_len); + free(drb->ul_pdcp_sn_len); +} + +/** + * @brief Encode F1 UE context setup request to ASN.1 + */ +F1AP_F1AP_PDU_t *encode_ue_context_setup_req(const f1ap_ue_context_setup_req_t *req) +{ + F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + + /* Message Type */ + pdu->present = F1AP_F1AP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, tmp); + tmp->procedureCode = F1AP_ProcedureCode_id_UEContextSetup; + tmp->criticality = F1AP_Criticality_reject; + tmp->value.present = F1AP_InitiatingMessage__value_PR_UEContextSetupRequest; + F1AP_UEContextSetupRequest_t *out = &tmp->value.choice.UEContextSetupRequest; + + /* mandatory: GNB_CU_UE_F1AP_ID */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupRequestIEs_t, ie1); + ie1->id = F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID; + ie1->criticality = F1AP_Criticality_reject; + ie1->value.present = F1AP_UEContextSetupRequestIEs__value_PR_GNB_CU_UE_F1AP_ID; + ie1->value.choice.GNB_CU_UE_F1AP_ID = req->gNB_CU_ue_id; + + /* optional: GNB_DU_UE_F1AP_ID */ + if (req->gNB_DU_ue_id) { + asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupRequestIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID; + ie->criticality = F1AP_Criticality_ignore; + ie->value.present = F1AP_UEContextSetupRequestIEs__value_PR_GNB_DU_UE_F1AP_ID; + ie->value.choice.GNB_DU_UE_F1AP_ID = *req->gNB_DU_ue_id; + } + + /* mandatory: SpCell ID/NRCGI */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupRequestIEs_t, ie3); + ie3->id = F1AP_ProtocolIE_ID_id_SpCell_ID; + ie3->criticality = F1AP_Criticality_reject; + ie3->value.present = F1AP_UEContextSetupRequestIEs__value_PR_NRCGI; + F1AP_NRCGI_t *nrcgi = &ie3->value.choice.NRCGI; + const plmn_id_t *plmn = &req->plmn; + MCC_MNC_TO_PLMNID(plmn->mcc,plmn->mnc,plmn->mnc_digit_length, &nrcgi->pLMN_Identity); + NR_CELL_ID_TO_BIT_STRING(req->nr_cellid, &nrcgi->nRCellIdentity); + + /* mandatory: ServCellIndox */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupRequestIEs_t, ie4); + ie4->id = F1AP_ProtocolIE_ID_id_ServCellIndex; + ie4->criticality = F1AP_Criticality_reject; + ie4->value.present = F1AP_UEContextSetupRequestIEs__value_PR_ServCellIndex; + ie4->value.choice.ServCellIndex = req->servCellIndex; + + /* mandatory: CUtoDURRCInformation */ + asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupRequestIEs_t, ie6); + ie6->id = F1AP_ProtocolIE_ID_id_CUtoDURRCInformation; + ie6->criticality = F1AP_Criticality_reject; + ie6->value.present = F1AP_UEContextSetupRequestIEs__value_PR_CUtoDURRCInformation; + ie6->value.choice.CUtoDURRCInformation = encode_cu_to_du_rrc_info(&req->cu_to_du_rrc_info); + + /* optional: SRB to be setup list */ + if (req->srbs_len > 0) { + asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupRequestIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_SRBs_ToBeSetup_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_UEContextSetupRequestIEs__value_PR_SRBs_ToBeSetup_List; + ie->value.choice.SRBs_ToBeSetup_List = encode_srbs_to_setup(req->srbs_len, req->srbs); + } + + /* optional: DRB to be setup list */ + if (req->drbs_len > 0) { + asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupRequestIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_DRBs_ToBeSetup_List; + ie->criticality = F1AP_Criticality_reject; + ie->value.present = F1AP_UEContextSetupRequestIEs__value_PR_DRBs_ToBeSetup_List; + ie->value.choice.DRBs_ToBeSetup_List = encode_drbs_to_setup(req->drbs_len, req->drbs); + } + + /* optional: RRC container */ + if (req->rrc_container) { + asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupRequestIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_RRCContainer; + ie->criticality = F1AP_Criticality_ignore; + ie->value.present = F1AP_UEContextSetupRequestIEs__value_PR_RRCContainer; + OCTET_STRING_fromBuf(&ie->value.choice.RRCContainer, (const char *)req->rrc_container->buf, req->rrc_container->len); + } + + /* conditional if DRB setup list: gNB-DU UE Aggregate Maximum Bit Rate Uplink */ + if (req->gnb_du_ue_agg_mbr_ul) { + asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupRequestIEs_t, ie); + ie->id = F1AP_ProtocolIE_ID_id_GNB_DU_UE_AMBR_UL; + ie->criticality = F1AP_Criticality_ignore; + ie->value.present = F1AP_UEContextSetupRequestIEs__value_PR_BitRate; + asn_long2INTEGER(&ie->value.choice.BitRate, *req->gnb_du_ue_agg_mbr_ul); + } + + return pdu; +} + +/** + * @brief Decode F1 UE Context Setup Request + */ +bool decode_ue_context_setup_req(const F1AP_F1AP_PDU_t *pdu, f1ap_ue_context_setup_req_t *out) +{ + DevAssert(out != NULL); + memset(out, 0, sizeof(*out)); + + F1AP_UEContextSetupRequest_t *in = &pdu->choice.initiatingMessage->value.choice.UEContextSetupRequest; + F1AP_UEContextSetupRequestIEs_t *ie; + + F1AP_LIB_FIND_IE(F1AP_UEContextSetupRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true); + F1AP_LIB_FIND_IE(F1AP_UEContextSetupRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_SpCell_ID, true); + F1AP_LIB_FIND_IE(F1AP_UEContextSetupRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_ServCellIndex, true); + F1AP_LIB_FIND_IE(F1AP_UEContextSetupRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_CUtoDURRCInformation, true); + + for (int i = 0; i < in->protocolIEs.list.count; ++i) { + ie = in->protocolIEs.list.array[i]; + AssertError(ie != NULL, return false, "in->protocolIEs.list.array[i] is NULL"); + switch (ie->id) { + case F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupRequestIEs__value_PR_GNB_CU_UE_F1AP_ID); + out->gNB_CU_ue_id = ie->value.choice.GNB_CU_UE_F1AP_ID; + break; + case F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupRequestIEs__value_PR_GNB_DU_UE_F1AP_ID); + _F1_MALLOC(out->gNB_DU_ue_id, ie->value.choice.GNB_DU_UE_F1AP_ID); + break; + case F1AP_ProtocolIE_ID_id_SpCell_ID: { + _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupRequestIEs__value_PR_NRCGI); + const F1AP_NRCGI_t *nrcgi = &ie->value.choice.NRCGI; + PLMNID_TO_MCC_MNC(&nrcgi->pLMN_Identity, out->plmn.mcc, out->plmn.mnc, out->plmn.mnc_digit_length); + BIT_STRING_TO_NR_CELL_IDENTITY(&nrcgi->nRCellIdentity, out->nr_cellid); + } break; + case F1AP_ProtocolIE_ID_id_ServCellIndex: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupRequestIEs__value_PR_ServCellIndex); + out->servCellIndex = ie->value.choice.ServCellIndex; + break; + case F1AP_ProtocolIE_ID_id_CUtoDURRCInformation: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupRequestIEs__value_PR_CUtoDURRCInformation); + _F1_CHECK_EXP(decode_cu_to_du_rrc_info(&out->cu_to_du_rrc_info, &ie->value.choice.CUtoDURRCInformation)); + break; + case F1AP_ProtocolIE_ID_id_SRBs_ToBeSetup_List: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupRequestIEs__value_PR_SRBs_ToBeSetup_List); + _F1_CHECK_EXP(decode_srbs_to_setup(&ie->value.choice.SRBs_ToBeSetup_List, &out->srbs_len, &out->srbs)); + break; + case F1AP_ProtocolIE_ID_id_DRBs_ToBeSetup_List: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupRequestIEs__value_PR_DRBs_ToBeSetup_List); + { + // gNB-DU UE Aggregate Maximum Bit Rate Uplink is C-ifDRBSetup + // below, check that gNB-DU UE AMBR UL IE is present if we have DRB + // setup, as required by the spec, or return false otherwise (like in + // the beginning of the function) + F1AP_UEContextSetupRequestIEs_t *check_ie; + F1AP_LIB_FIND_IE(F1AP_UEContextSetupRequestIEs_t, check_ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_GNB_DU_UE_AMBR_UL, true); + } + _F1_CHECK_EXP(decode_drbs_to_setup(&ie->value.choice.DRBs_ToBeSetup_List, &out->drbs_len, &out->drbs)); + break; + case F1AP_ProtocolIE_ID_id_RRCContainer: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupRequestIEs__value_PR_RRCContainer); + out->rrc_container = malloc_or_fail(sizeof(*out->rrc_container)); + { + OCTET_STRING_t *os = &ie->value.choice.RRCContainer; + *out->rrc_container = create_byte_array(os->size, os->buf); + } + break; + case F1AP_ProtocolIE_ID_id_GNB_DU_UE_AMBR_UL: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupRequestIEs__value_PR_BitRate); + out->gnb_du_ue_agg_mbr_ul = malloc_or_fail(sizeof(*out->gnb_du_ue_agg_mbr_ul)); + asn_INTEGER2uint64(&ie->value.choice.BitRate, out->gnb_du_ue_agg_mbr_ul); + break; + default: + PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); + break; + } + } + + return true; +} + +/** + * @brief F1 UE Context Setup Request deep copy + */ +f1ap_ue_context_setup_req_t cp_ue_context_setup_req(const f1ap_ue_context_setup_req_t *orig) +{ + /* copy all mandatory fields that are not dynamic memory */ + f1ap_ue_context_setup_req_t cp = { + .gNB_CU_ue_id = orig->gNB_CU_ue_id, + .plmn = orig->plmn, + .nr_cellid = orig->nr_cellid, + .servCellIndex = orig->servCellIndex, + .cu_to_du_rrc_info = cp_cu_to_du_rrc_info(&orig->cu_to_du_rrc_info), + }; + if (orig->gNB_DU_ue_id) + _F1_MALLOC(cp.gNB_DU_ue_id, *orig->gNB_DU_ue_id); + if (orig->srbs_len > 0 && orig->srbs) { + cp.srbs = calloc_or_fail(orig->srbs_len, sizeof(*cp.srbs)); + cp.srbs_len = orig->srbs_len; + for (int i = 0; i < cp.srbs_len; ++i) + cp.srbs[i] = cp_srb_to_setup(&orig->srbs[i]); + } + if (orig->drbs_len > 0 && orig->drbs) { + cp.drbs = calloc_or_fail(orig->drbs_len, sizeof(*cp.drbs)); + cp.drbs_len = orig->drbs_len; + for (int i = 0; i < cp.drbs_len; ++i) + cp.drbs[i] = cp_drb_to_setup(&orig->drbs[i]); + } + CP_OPT_BYTE_ARRAY(cp.rrc_container, orig->rrc_container); + if (orig->gnb_du_ue_agg_mbr_ul) + _F1_MALLOC(cp.gnb_du_ue_agg_mbr_ul, *orig->gnb_du_ue_agg_mbr_ul); + return cp; +} + +/** + * @brief F1 UE Context Setup Request equality check + */ +bool eq_ue_context_setup_req(const f1ap_ue_context_setup_req_t *a, const f1ap_ue_context_setup_req_t *b) +{ + _F1_EQ_CHECK_INT(a->gNB_CU_ue_id, b->gNB_CU_ue_id); + _F1_EQ_CHECK_OPTIONAL_IE(a, b, gNB_DU_ue_id, _F1_EQ_CHECK_INT); + _F1_CHECK_EXP(eq_f1ap_plmn(&a->plmn, &b->plmn)); + _F1_EQ_CHECK_LONG(a->nr_cellid, b->nr_cellid); + _F1_EQ_CHECK_INT(a->servCellIndex, b->servCellIndex); + _F1_CHECK_EXP(eq_cu_to_du_rrc_info(&a->cu_to_du_rrc_info, &b->cu_to_du_rrc_info)); + + _F1_EQ_CHECK_INT(a->srbs_len, b->srbs_len); + _F1_CHECK_EXP(a->srbs_len == 0 || (a->srbs && b->srbs)); + for (int i = 0; i < a->srbs_len; ++i) + _F1_CHECK_EXP(eq_srb_to_setup(&a->srbs[i], &b->srbs[i])); + + _F1_EQ_CHECK_INT(a->drbs_len, b->drbs_len); + _F1_CHECK_EXP(a->drbs_len == 0 || (a->drbs && b->drbs)); + for (int i = 0; i < a->drbs_len; ++i) + _F1_CHECK_EXP(eq_drb_to_setup(&a->drbs[i], &b->drbs[i])); + + _F1_EQ_CHECK_OPTIONAL_IE(a, b, rrc_container, eq_ba); + + _F1_EQ_CHECK_OPTIONAL_IE(a, b, gnb_du_ue_agg_mbr_ul, _F1_EQ_CHECK_LONG); + return true; +} + +/** + * @brief Free Allocated F1 UE Context Setup Request + */ +void free_ue_context_setup_req(f1ap_ue_context_setup_req_t *req) +{ + free(req->gNB_DU_ue_id); + free_cu_to_du_rrc_info(&req->cu_to_du_rrc_info); + for (int i = 0; i < req->srbs_len; ++i) + free_srb_to_setup(&req->srbs[i]); + free(req->srbs); + for (int i = 0; i < req->drbs_len; ++i) + free_drb_to_setup(&req->drbs[i]); + free(req->drbs); + FREE_OPT_BYTE_ARRAY(req->rrc_container); + free(req->gnb_du_ue_agg_mbr_ul); +} diff --git a/openair2/F1AP/lib/f1ap_ue_context.h b/openair2/F1AP/lib/f1ap_ue_context.h new file mode 100644 index 0000000000..947273b7a8 --- /dev/null +++ b/openair2/F1AP/lib/f1ap_ue_context.h @@ -0,0 +1,36 @@ +/* + * 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 F1AP_UE_CONTEXT_SETUP_REQ_H_ +#define F1AP_UE_CONTEXT_SETUP_REQ_H_ + +#include +#include "f1ap_messages_types.h" + +struct F1AP_F1AP_PDU; + +struct F1AP_F1AP_PDU *encode_ue_context_setup_req(const f1ap_ue_context_setup_req_t *msg); +bool decode_ue_context_setup_req(const struct F1AP_F1AP_PDU *pdu, f1ap_ue_context_setup_req_t *out); +f1ap_ue_context_setup_req_t cp_ue_context_setup_req(const f1ap_ue_context_setup_req_t *orig); +bool eq_ue_context_setup_req(const f1ap_ue_context_setup_req_t *a, const f1ap_ue_context_setup_req_t *b); +void free_ue_context_setup_req(f1ap_ue_context_setup_req_t *req); + +#endif /* F1AP_UE_CONTEXT_SETUP_REQ_H_ */ diff --git a/openair2/F1AP/tests/CMakeLists.txt b/openair2/F1AP/tests/CMakeLists.txt index a73334051a..396e86d513 100644 --- a/openair2/F1AP/tests/CMakeLists.txt +++ b/openair2/F1AP/tests/CMakeLists.txt @@ -1,6 +1,6 @@ add_executable(f1ap_lib_test f1ap_lib_test.c) add_dependencies(tests f1ap_lib_test) add_test(NAME f1ap_lib_test COMMAND f1ap_lib_test) -target_link_libraries(f1ap_lib_test PRIVATE f1ap_lib) +target_link_libraries(f1ap_lib_test PRIVATE f1ap_lib ds) target_link_libraries(f1ap_lib_test PRIVATE asn1_f1ap) target_include_directories(f1ap_lib_test PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index 57a503d8ab..a06bd28564 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "common/utils/assertions.h" #include "common/utils/utils.h" @@ -38,8 +39,10 @@ #include "f1ap_messages_types.h" #include "F1AP_F1AP-PDU.h" +#include "lib/f1ap_lib_common.h" #include "lib/f1ap_rrc_message_transfer.h" #include "lib/f1ap_interface_management.h" +#include "lib/f1ap_ue_context.h" void exit_function(const char *file, const char *function, const int line, const char *s, const int assert) { @@ -718,6 +721,171 @@ static void test_f1ap_cu_configuration_update_acknowledge(void) // No free needed } +static byte_array_t get_test_ba(const char *s) +{ + byte_array_t ba = { + .len = strlen(s) + 1, /* binary data: needs to include null-byte of strdup() */ + .buf = (uint8_t *) strdup(s), + }; + return ba; +} + +static byte_array_t *get_malloced_test_ba(const char *s) +{ + byte_array_t *ba = calloc_or_fail(1, sizeof(*ba)); + *ba = get_test_ba(s); + return ba; +} + +static void test_f1ap_ue_context_setup_request() +{ + plmn_id_t plmn = { .mcc = 001, .mnc = 01, .mnc_digit_length = 2 }; + f1ap_ue_context_setup_req_t orig = { + .gNB_CU_ue_id = 12, + //.gNB_DU_ue_id below + .plmn = plmn, + .nr_cellid = 132, + .servCellIndex = 1, + //.cu_to_du_rrc_info below + //.srbs below + //.drbs below + //gnb_du_ue_agg_mbr_ul below + }; + _F1_MALLOC(orig.gNB_DU_ue_id, 13); + orig.cu_to_du_rrc_info.cg_configinfo = get_malloced_test_ba("CG-CONFIGINFO"); + orig.cu_to_du_rrc_info.ue_cap = get_malloced_test_ba("UE CAP"); + orig.cu_to_du_rrc_info.meas_config = get_malloced_test_ba("UE MEASUREMENT CONFIGURATION"); + orig.cu_to_du_rrc_info.ho_prep_info = get_malloced_test_ba("PREPARATION DATA FOR HANDOVER"); + orig.srbs_len = 2; + orig.srbs = calloc_or_fail(orig.srbs_len, sizeof(*orig.srbs)); + for (int i = 0; i < orig.srbs_len; ++i) + orig.srbs[i].id = 1 + i; + + orig.drbs_len = 2; + orig.drbs = calloc_or_fail(orig.drbs_len, sizeof(*orig.drbs)); + f1ap_drb_to_setup_t *drb1 = &orig.drbs[0]; + drb1->id = 4; + drb1->qos_choice = F1AP_QOS_CHOICE_NR; + f1ap_arp_t arp = { 1, MAY_TRIGGER_PREEMPTION, PREEMPTABLE, }; + drb1->nr.drb_qos.qos_type = NON_DYNAMIC; + drb1->nr.drb_qos.nondyn.fiveQI = 3; + drb1->nr.drb_qos.arp = arp; + drb1->nr.nssai = (nssai_t) {.sst = 1, .sd = 123}; + drb1->nr.flows_len = 1; + f1ap_drb_flows_mapped_t *flow = drb1->nr.flows = calloc_or_fail(drb1->nr.flows_len, sizeof(*flow)); + flow->qfi = 2; + flow->param.qos_type = NON_DYNAMIC; + flow->param.nondyn.fiveQI = 3; + flow->param.arp = arp; + drb1->up_ul_tnl_len = 1; + inet_pton(AF_INET, "192.168.40.23", &drb1->up_ul_tnl[0].tl_address); + drb1->up_ul_tnl[0].teid = 0x11223344; + drb1->rlc_mode = F1AP_RLC_MODE_UM_BIDIR; + _F1_MALLOC(drb1->dl_pdcp_sn_len, F1AP_PDCP_SN_18B); + _F1_MALLOC(drb1->ul_pdcp_sn_len, F1AP_PDCP_SN_18B); + + f1ap_drb_to_setup_t *drb2 = &orig.drbs[1]; + drb2->id = 12; + drb2->qos_choice = F1AP_QOS_CHOICE_NR; + drb2->nr.drb_qos.qos_type = DYNAMIC; + f1ap_dynamic_5qi_t *dyn = &drb2->nr.drb_qos.dyn; + dyn->prio = 4; + dyn->pdb = 1000; + dyn->per.scalar = 4; + dyn->per.exponent = 8; + _F1_MALLOC(dyn->delay_critical, true); + _F1_MALLOC(dyn->avg_win, 3000); + f1ap_arp_t arp2 = { 13, SHALL_NOT_TRIGGER_PREEMPTION, NOT_PREEMPTABLE, }; + drb2->nr.drb_qos.arp = arp2; + drb2->nr.nssai = (nssai_t) {.sst = 2, .sd = 0xffffff}; + drb2->nr.flows_len = 2; + f1ap_drb_flows_mapped_t *flow2 = drb2->nr.flows = calloc_or_fail(drb2->nr.flows_len, sizeof(*flow2)); + flow2[0].qfi = 4; + flow2[0].param.qos_type = DYNAMIC; + flow2[0].param.dyn.prio = 4; + flow2[0].param.dyn.pdb = 999; + flow2[0].param.dyn.per.scalar = 4; + flow2[0].param.dyn.per.exponent = 8; + flow2[0].param.arp = arp2; + flow2[1].qfi = 5; + flow2[1].param.qos_type = NON_DYNAMIC; + flow2[1].param.nondyn.fiveQI = 3; + flow2[1].param.arp = arp; + drb2->up_ul_tnl_len = 2; + inet_pton(AF_INET, "10.0.0.2", &drb2->up_ul_tnl[0].tl_address); + drb2->up_ul_tnl[0].teid = 0x1234; + inet_pton(AF_INET, "10.0.0.2", &drb2->up_ul_tnl[1].tl_address); + drb2->up_ul_tnl[1].teid = 0x1235; + drb2->rlc_mode = F1AP_RLC_MODE_AM; + _F1_MALLOC(drb2->dl_pdcp_sn_len, F1AP_PDCP_SN_12B); + _F1_MALLOC(drb2->ul_pdcp_sn_len, F1AP_PDCP_SN_12B); + + orig.rrc_container = get_malloced_test_ba("RRC container"); + + _F1_MALLOC(orig.gnb_du_ue_agg_mbr_ul, 5LL * 1000 * 1000 * 1000); + + F1AP_F1AP_PDU_t *f1enc = encode_ue_context_setup_req(&orig); + F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); + f1ap_msg_free(f1enc); + + f1ap_ue_context_setup_req_t decoded = {0}; + bool ret = decode_ue_context_setup_req(f1dec, &decoded); + AssertFatal(ret, "decode_ue_context_setup_req(): could not decode message\n"); + f1ap_msg_free(f1dec); + + ret = eq_ue_context_setup_req(&orig, &decoded); + AssertFatal(ret, "eq_ue_context_setup_req(): decoded message doesn't match\n"); + char ip_ret[100]; + inet_ntop(AF_INET, &decoded.drbs[0].up_ul_tnl[0].tl_address, ip_ret, sizeof(ip_ret)); + printf("%s\n", ip_ret); + inet_ntop(AF_INET, &decoded.drbs[1].up_ul_tnl[0].tl_address, ip_ret, sizeof(ip_ret)); + printf("%s\n", ip_ret); + free_ue_context_setup_req(&decoded); + + f1ap_ue_context_setup_req_t cp = cp_ue_context_setup_req(&orig); + ret = eq_ue_context_setup_req(&orig, &cp); + AssertFatal(ret, "eq_ue_context_setup_req(): copied message doesn't match\n"); + free_ue_context_setup_req(&orig); + free_ue_context_setup_req(&cp); + + printf("%s() successful\n", __func__); +} + +static void test_f1ap_ue_context_setup_request_simple() +{ + plmn_id_t plmn = { .mcc = 001, .mnc = 01, .mnc_digit_length = 2 }; + f1ap_ue_context_setup_req_t orig = { + .gNB_CU_ue_id = 12, + .plmn = plmn, + .nr_cellid = 132, + .servCellIndex = 1, + //.cu_to_du_rrc_info: all fields optional, intentionally left empty + // rest is optional and intentionally left empty + // .gnb_du_ue_agg_mbr_ul is C-ifDRBSetup => no DRB => no IE + }; + F1AP_F1AP_PDU_t *f1enc = encode_ue_context_setup_req(&orig); + F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); + f1ap_msg_free(f1enc); + + f1ap_ue_context_setup_req_t decoded = {0}; + bool ret = decode_ue_context_setup_req(f1dec, &decoded); + AssertFatal(ret, "decode_ue_context_setup_req(): could not decode message\n"); + f1ap_msg_free(f1dec); + + ret = eq_ue_context_setup_req(&orig, &decoded); + AssertFatal(ret, "eq_ue_context_setup_req(): decoded message doesn't match\n"); + free_ue_context_setup_req(&decoded); + + f1ap_ue_context_setup_req_t cp = cp_ue_context_setup_req(&orig); + ret = eq_ue_context_setup_req(&orig, &cp); + AssertFatal(ret, "eq_ue_context_setup_req(): copied message doesn't match\n"); + free_ue_context_setup_req(&orig); + free_ue_context_setup_req(&cp); + + printf("%s() successful\n", __func__); +} + + int main() { test_initial_ul_rrc_message_transfer(); @@ -733,5 +901,7 @@ int main() test_f1ap_cu_configuration_update(); test_f1ap_cu_configuration_update_acknowledge(); test_f1ap_du_configuration_update_acknowledge(); + test_f1ap_ue_context_setup_request(); + test_f1ap_ue_context_setup_request_simple(); return 0; }