/* * 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 */ F1AP_ProtocolExtensionContainer_10696P60_t *p = NULL; if (cu2du->ho_prep_info) { 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); } /* optional: iE_Extensions */ if (cu2du->meas_timing_config) { if (!p) 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_MeasurementTimingConfiguration; ie_ext->criticality = F1AP_Criticality_ignore; ie_ext->extensionValue.present = F1AP_CUtoDURRCInformation_ExtIEs__extensionValue_PR_MeasurementTimingConfiguration; const byte_array_t *ba = cu2du->meas_timing_config; F1AP_MeasurementTimingConfiguration_t *mtc = &ie_ext->extensionValue.choice.MeasurementTimingConfiguration; OCTET_STRING_fromBuf(mtc, (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; case F1AP_ProtocolIE_ID_id_MeasurementTimingConfiguration: dec->meas_timing_config = calloc_or_fail(1, sizeof(*dec->meas_timing_config)); const F1AP_MeasurementTimingConfiguration_t *mtc = &cu2du_info_ext->extensionValue.choice.MeasurementTimingConfiguration; *dec->meas_timing_config = create_byte_array(mtc->size, (uint8_t *)mtc->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.meas_timing_config, src->meas_timing_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, meas_timing_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->meas_timing_config); FREE_OPT_BYTE_ARRAY(cu2du->ho_prep_info); } static F1AP_DUtoCURRCInformation_t encode_du_to_cu_rrc_info(const f1ap_du_to_cu_rrc_info_t *du2cu) { F1AP_DUtoCURRCInformation_t enc = {0}; /* mandatory: cellGroupConfig */ const byte_array_t *ba = &du2cu->cell_group_config; OCTET_STRING_fromBuf(&enc.cellGroupConfig, (const char *)ba->buf, ba->len); /* optional: MeasGapConfig */ if (du2cu->meas_gap_config) { ba = du2cu->meas_gap_config; asn1cCalloc(enc.measGapConfig, measGapConfig); OCTET_STRING_fromBuf(measGapConfig, (const char *)ba->buf, ba->len); } return enc; } static bool decode_du_to_cu_rrc_info(f1ap_du_to_cu_rrc_info_t *dec, const F1AP_DUtoCURRCInformation_t *du2cu) { const F1AP_CellGroupConfig_t *cgc = &du2cu->cellGroupConfig; dec->cell_group_config = create_byte_array(cgc->size, (uint8_t *)cgc->buf); if (du2cu->measGapConfig) { const F1AP_MeasGapConfig_t *mgc = du2cu->measGapConfig; dec->meas_gap_config = calloc_or_fail(1, sizeof(*dec->meas_gap_config)); *dec->meas_gap_config = create_byte_array(mgc->size, (uint8_t *)mgc->buf); } return true; } static f1ap_du_to_cu_rrc_info_t cp_du_to_cu_rrc_info(const f1ap_du_to_cu_rrc_info_t *src) { f1ap_du_to_cu_rrc_info_t cp = {0}; cp.cell_group_config = copy_byte_array(src->cell_group_config); CP_OPT_BYTE_ARRAY(cp.meas_gap_config, src->meas_gap_config); return cp; } static bool eq_du_to_cu_rrc_info(const f1ap_du_to_cu_rrc_info_t *a, const f1ap_du_to_cu_rrc_info_t *b) { _F1_CHECK_EXP(eq_byte_array(&a->cell_group_config, &b->cell_group_config)); _F1_EQ_CHECK_OPTIONAL_IE(a, b, meas_gap_config, eq_ba); return true; } static void free_du_to_cu_rrc_info(f1ap_du_to_cu_rrc_info_t *du2cu) { free_byte_array(du2cu->cell_group_config); FREE_OPT_BYTE_ARRAY(du2cu->meas_gap_config); } /* \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 Encode SRB-ToBeSetupMod List, for UE context Modification Request, * from f1ap_srb_to_setup_t. The ToBeSetup/ToBeSetupMod lists are basically the * same in both cases, but the asn1c types are different. */ static F1AP_SRBs_ToBeSetupMod_List_t encode_srbs_to_setupmod(int n, const f1ap_srb_to_setup_t *srbs) { F1AP_SRBs_ToBeSetupMod_List_t list = {0}; for (int i = 0; i < n; ++i) { asn1cSequenceAdd(list, F1AP_SRBs_ToBeSetupMod_ItemIEs_t, itie); itie->id = F1AP_ProtocolIE_ID_id_SRBs_ToBeSetupMod_Item; itie->criticality = F1AP_Criticality_reject; itie->value.present = F1AP_SRBs_ToBeSetupMod_ItemIEs__value_PR_SRBs_ToBeSetupMod_Item; itie->value.choice.SRBs_ToBeSetupMod_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; } /* \brief Decode SRB-ToBeSetupMod List, from UE Context Modification Request, * into f1ap_srb_to_setup_t. The ToBeSetup/ToBeSetupMod lists are basically the * same, but the asn1c types are different. */ static bool decode_srbs_to_setupmod(const F1AP_SRBs_ToBeSetupMod_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_ToBeSetupMod_ItemIEs_t *itie = (F1AP_SRBs_ToBeSetupMod_ItemIEs_t *)f1ap->list.array[i]; _F1_EQ_CHECK_LONG(itie->id, F1AP_ProtocolIE_ID_id_SRBs_ToBeSetupMod_Item); _F1_EQ_CHECK_INT(itie->value.present, F1AP_SRBs_ToBeSetupMod_ItemIEs__value_PR_SRBs_ToBeSetupMod_Item); srbs[i].id = itie->value.choice.SRBs_ToBeSetupMod_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_SRBs_Setup_List_t encode_srbs_setup(int n, const f1ap_srb_setup_t *srbs) { F1AP_SRBs_Setup_List_t list = {0}; for (int i = 0; i < n; ++i) { asn1cSequenceAdd(list, F1AP_SRBs_Setup_ItemIEs_t, itie); itie->id = F1AP_ProtocolIE_ID_id_SRBs_Setup_Item; itie->criticality = F1AP_Criticality_ignore; itie->value.present = F1AP_SRBs_Setup_ItemIEs__value_PR_SRBs_Setup_Item; F1AP_SRBs_Setup_Item_t *it = &itie->value.choice.SRBs_Setup_Item; it->sRBID = srbs[i].id; it->lCID = srbs[i].lcid; } return list; } static F1AP_SRBs_SetupMod_List_t encode_srbs_setupmod(int n, const f1ap_srb_setup_t *srbs) { F1AP_SRBs_SetupMod_List_t list = {0}; for (int i = 0; i < n; ++i) { asn1cSequenceAdd(list, F1AP_SRBs_SetupMod_ItemIEs_t, itie); itie->id = F1AP_ProtocolIE_ID_id_SRBs_SetupMod_Item; itie->criticality = F1AP_Criticality_ignore; itie->value.present = F1AP_SRBs_SetupMod_ItemIEs__value_PR_SRBs_SetupMod_Item; F1AP_SRBs_SetupMod_Item_t *it = &itie->value.choice.SRBs_SetupMod_Item; it->sRBID = srbs[i].id; it->lCID = srbs[i].lcid; } return list; } static bool decode_srbs_setup(const F1AP_SRBs_Setup_List_t *f1ap, int *n, f1ap_srb_setup_t **out) { DevAssert(out != NULL); *out = NULL; int count = *n = f1ap->list.count; if (count == 0) return true; f1ap_srb_setup_t *srbs = *out = calloc_or_fail(count, sizeof(*srbs)); for (int i = 0; i < count; ++i) { const F1AP_SRBs_Setup_ItemIEs_t *itie = (F1AP_SRBs_Setup_ItemIEs_t *)f1ap->list.array[i]; _F1_EQ_CHECK_LONG(itie->id, F1AP_ProtocolIE_ID_id_SRBs_Setup_Item); _F1_EQ_CHECK_INT(itie->value.present, F1AP_SRBs_Setup_ItemIEs__value_PR_SRBs_Setup_Item); const F1AP_SRBs_Setup_Item_t *it = &itie->value.choice.SRBs_Setup_Item; srbs[i].id = it->sRBID; srbs[i].lcid = it->lCID; } return true; } static bool decode_srbs_setupmod(const F1AP_SRBs_SetupMod_List_t *f1ap, int *n, f1ap_srb_setup_t **out) { DevAssert(out != NULL); *out = NULL; int count = *n = f1ap->list.count; if (count == 0) return true; f1ap_srb_setup_t *srbs = *out = calloc_or_fail(count, sizeof(*srbs)); for (int i = 0; i < count; ++i) { const F1AP_SRBs_SetupMod_ItemIEs_t *itie = (F1AP_SRBs_SetupMod_ItemIEs_t *)f1ap->list.array[i]; _F1_EQ_CHECK_LONG(itie->id, F1AP_ProtocolIE_ID_id_SRBs_SetupMod_Item); _F1_EQ_CHECK_INT(itie->value.present, F1AP_SRBs_SetupMod_ItemIEs__value_PR_SRBs_SetupMod_Item); const F1AP_SRBs_SetupMod_Item_t *it = &itie->value.choice.SRBs_SetupMod_Item; srbs[i].id = it->sRBID; srbs[i].lcid = it->lCID; } return true; } static f1ap_srb_setup_t cp_srb_setup(const f1ap_srb_setup_t *orig) { f1ap_srb_setup_t cp = {.id = orig->id, .lcid = orig->lcid}; return cp; } static bool eq_srb_setup(const f1ap_srb_setup_t *a, const f1ap_srb_setup_t *b) { _F1_EQ_CHECK_INT(a->id, b->id); _F1_EQ_CHECK_INT(a->lcid, b->lcid); return true; } static void free_srb_setup(f1ap_srb_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; } /* Encode a DRBs_ToBeSetupMod list for UE Context Modification Request, from * f1ap_drb_to_setup_t. The ToBeSetup/ToBeSetupMod lists are almost the same * (with the exception that one field is optional in the latter), so that we * can reuse the internal types, but the asn1c types are different. */ static F1AP_DRBs_ToBeSetupMod_List_t encode_drbs_to_setupmod(int n, const f1ap_drb_to_setup_t *drbs) { F1AP_DRBs_ToBeSetupMod_List_t list = {0}; for (int i = 0; i < n; ++i) { const f1ap_drb_to_setup_t *drb = &drbs[i]; asn1cSequenceAdd(list, F1AP_DRBs_ToBeSetupMod_ItemIEs_t, itie); itie->id = F1AP_ProtocolIE_ID_id_DRBs_ToBeSetupMod_Item; itie->criticality = F1AP_Criticality_reject; itie->value.present = F1AP_DRBs_ToBeSetupMod_ItemIEs__value_PR_DRBs_ToBeSetupMod_Item; F1AP_DRBs_ToBeSetupMod_Item_t *it = &itie->value.choice.DRBs_ToBeSetupMod_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_ToBeSetupMod_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); if (!drb->dl_pdcp_sn_len && !drb->ul_pdcp_sn_len) continue; F1AP_ProtocolExtensionContainer_10696P83_t *ext = calloc_or_fail(1, sizeof(*ext)); it->iE_Extensions = (struct F1AP_ProtocolExtensionContainer *)ext; if (drb->dl_pdcp_sn_len) { asn1cSequenceAdd(ext->list, F1AP_DRBs_ToBeSetupMod_ItemExtIEs_t, ext_ie); ext_ie->id = F1AP_ProtocolIE_ID_id_DLPDCPSNLength; ext_ie->criticality = F1AP_Criticality_ignore; ext_ie->extensionValue.present = F1AP_DRBs_ToBeSetupMod_ItemExtIEs__extensionValue_PR_PDCPSNLength; ext_ie->extensionValue.choice.PDCPSNLength = *drb->dl_pdcp_sn_len; } if (drb->ul_pdcp_sn_len) { asn1cSequenceAdd(ext->list, F1AP_DRBs_ToBeSetupMod_ItemExtIEs_t, ext_ie); ext_ie->id = F1AP_ProtocolIE_ID_id_ULPDCPSNLength; ext_ie->criticality = F1AP_Criticality_ignore; ext_ie->extensionValue.present = F1AP_DRBs_ToBeSetupMod_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; } /* \brief Decode DRBS-ToBeSetupMod list from UE Context Modification Request, into * f1ap_drb_to_setup_t. The ToBeSetup/ToBeSetupMod lists are almost the same * (except for some optional types in the latter), so we reuse the internal * representation, but the asn1c types are different. */ static bool decode_drbs_to_setupmod(const F1AP_DRBs_ToBeSetupMod_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_ToBeSetupMod_ItemIEs_t *itie = (const F1AP_DRBs_ToBeSetupMod_ItemIEs_t *)f1ap->list.array[i]; _F1_EQ_CHECK_LONG(itie->id, F1AP_ProtocolIE_ID_id_DRBs_ToBeSetupMod_Item); _F1_EQ_CHECK_INT(itie->value.present, F1AP_DRBs_ToBeSetupMod_ItemIEs__value_PR_DRBs_ToBeSetupMod_Item); const F1AP_DRBs_ToBeSetupMod_Item_t *it = &itie->value.choice.DRBs_ToBeSetupMod_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)); if (!it->iE_Extensions) continue; const F1AP_ProtocolExtensionContainer_10696P83_t *ext = (const F1AP_ProtocolExtensionContainer_10696P83_t *)it->iE_Extensions; const F1AP_DRBs_ToBeSetupMod_ItemExtIEs_t *ie; 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_ToBeSetupMod_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_ToBeSetupMod_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); } static F1AP_DRBs_Setup_List_t encode_drbs_setup(int n, const f1ap_drb_setup_t *drbs) { F1AP_DRBs_Setup_List_t list = {0}; for (int i = 0; i < n; ++i) { const f1ap_drb_setup_t *drb = &drbs[i]; asn1cSequenceAdd(list, F1AP_DRBs_Setup_ItemIEs_t, itie); itie->id = F1AP_ProtocolIE_ID_id_DRBs_Setup_Item; itie->criticality = F1AP_Criticality_ignore; itie->value.present = F1AP_DRBs_Setup_ItemIEs__value_PR_DRBs_Setup_Item; F1AP_DRBs_Setup_Item_t *it = &itie->value.choice.DRBs_Setup_Item; it->dRBID = drb->id; if (drb->lcid) asn1cCallocOne(it->lCID, *drb->lcid); for (int j = 0; j < drb->up_dl_tnl_len; ++j) { DevAssert(drb->up_dl_tnl[j].teid > 0); asn1cSequenceAdd(it->dLUPTNLInformation_ToBeSetup_List.list, F1AP_DLUPTNLInformation_ToBeSetup_Item_t, tnl_it); tnl_it->dLUPTNLInformation = encode_up_tnl(&drb->up_dl_tnl[j]); } } return list; } static F1AP_DRBs_SetupMod_List_t encode_drbs_setupmod(int n, const f1ap_drb_setup_t *drbs) { F1AP_DRBs_SetupMod_List_t list = {0}; for (int i = 0; i < n; ++i) { const f1ap_drb_setup_t *drb = &drbs[i]; asn1cSequenceAdd(list, F1AP_DRBs_SetupMod_ItemIEs_t, itie); itie->id = F1AP_ProtocolIE_ID_id_DRBs_SetupMod_Item; itie->criticality = F1AP_Criticality_ignore; itie->value.present = F1AP_DRBs_SetupMod_ItemIEs__value_PR_DRBs_SetupMod_Item; F1AP_DRBs_SetupMod_Item_t *it = &itie->value.choice.DRBs_SetupMod_Item; it->dRBID = drb->id; if (drb->lcid) asn1cCallocOne(it->lCID, *drb->lcid); for (int j = 0; j < drb->up_dl_tnl_len; ++j) { DevAssert(drb->up_dl_tnl[j].teid > 0); asn1cSequenceAdd(it->dLUPTNLInformation_ToBeSetup_List.list, F1AP_DLUPTNLInformation_ToBeSetup_Item_t, tnl_it); tnl_it->dLUPTNLInformation = encode_up_tnl(&drb->up_dl_tnl[j]); } } return list; } static bool decode_drbs_setup(const F1AP_DRBs_Setup_List_t *f1ap, int *n, f1ap_drb_setup_t **out) { DevAssert(out != NULL); *out = NULL; int count = *n = f1ap->list.count; if (count == 0) return true; f1ap_drb_setup_t *drbs = *out = calloc_or_fail(count, sizeof(*drbs)); for (int i = 0; i < count; ++i) { const F1AP_DRBs_Setup_ItemIEs_t *itie = (const F1AP_DRBs_Setup_ItemIEs_t *)f1ap->list.array[i]; _F1_EQ_CHECK_LONG(itie->id, F1AP_ProtocolIE_ID_id_DRBs_Setup_Item); _F1_EQ_CHECK_INT(itie->value.present, F1AP_DRBs_Setup_ItemIEs__value_PR_DRBs_Setup_Item); const F1AP_DRBs_Setup_Item_t *it = &itie->value.choice.DRBs_Setup_Item; f1ap_drb_setup_t *drb = &drbs[i]; drb->id = it->dRBID; if (it->lCID) _F1_MALLOC(drb->lcid, *it->lCID); drb->up_dl_tnl_len = it->dLUPTNLInformation_ToBeSetup_List.list.count; _F1_EQ_CHECK_GENERIC(drb->up_dl_tnl_len > 0 && drb->up_dl_tnl_len <= 2, "%d", drb->up_dl_tnl_len, 1); for (int j = 0; j < drb->up_dl_tnl_len; ++j) { F1AP_DLUPTNLInformation_ToBeSetup_Item_t *it_tnl = it->dLUPTNLInformation_ToBeSetup_List.list.array[j]; _F1_CHECK_EXP(decode_up_tnl(&it_tnl->dLUPTNLInformation, &drb->up_dl_tnl[j])); } } return true; } static bool decode_drbs_setupmod(const F1AP_DRBs_SetupMod_List_t *f1ap, int *n, f1ap_drb_setup_t **out) { DevAssert(out != NULL); *out = NULL; int count = *n = f1ap->list.count; if (count == 0) return true; f1ap_drb_setup_t *drbs = *out = calloc_or_fail(count, sizeof(*drbs)); for (int i = 0; i < count; ++i) { const F1AP_DRBs_SetupMod_ItemIEs_t *itie = (const F1AP_DRBs_SetupMod_ItemIEs_t *)f1ap->list.array[i]; _F1_EQ_CHECK_LONG(itie->id, F1AP_ProtocolIE_ID_id_DRBs_SetupMod_Item); _F1_EQ_CHECK_INT(itie->value.present, F1AP_DRBs_SetupMod_ItemIEs__value_PR_DRBs_SetupMod_Item); const F1AP_DRBs_SetupMod_Item_t *it = &itie->value.choice.DRBs_SetupMod_Item; f1ap_drb_setup_t *drb = &drbs[i]; drb->id = it->dRBID; if (it->lCID) _F1_MALLOC(drb->lcid, *it->lCID); drb->up_dl_tnl_len = it->dLUPTNLInformation_ToBeSetup_List.list.count; _F1_EQ_CHECK_GENERIC(drb->up_dl_tnl_len > 0 && drb->up_dl_tnl_len <= 2, "%d", drb->up_dl_tnl_len, 1); for (int j = 0; j < drb->up_dl_tnl_len; ++j) { F1AP_DLUPTNLInformation_ToBeSetup_Item_t *it_tnl = it->dLUPTNLInformation_ToBeSetup_List.list.array[j]; _F1_CHECK_EXP(decode_up_tnl(&it_tnl->dLUPTNLInformation, &drb->up_dl_tnl[j])); } } return true; } static f1ap_drb_setup_t cp_drb_setup(const f1ap_drb_setup_t *orig) { f1ap_drb_setup_t cp = { .id = orig->id, .up_dl_tnl_len = orig->up_dl_tnl_len, .up_dl_tnl[0] = cp_up_tnl(&orig->up_dl_tnl[0]), .up_dl_tnl[1] = cp_up_tnl(&orig->up_dl_tnl[1]), }; if (orig->lcid) _F1_MALLOC(cp.lcid, *orig->lcid); return cp; } static bool eq_drb_setup(const f1ap_drb_setup_t *a, const f1ap_drb_setup_t *b) { _F1_EQ_CHECK_INT(a->id, b->id); _F1_EQ_CHECK_OPTIONAL_IE(a, b, lcid, _F1_EQ_CHECK_INT); _F1_EQ_CHECK_INT(a->up_dl_tnl_len, b->up_dl_tnl_len); for (int i = 0; i < a->up_dl_tnl_len; ++i) _F1_CHECK_EXP(eq_up_tnl(&a->up_dl_tnl[i], &b->up_dl_tnl[i])); return true; } static void free_drb_setup(f1ap_drb_setup_t *drb) { free(drb->lcid); } static F1AP_DRBs_ToBeReleased_List_t encode_drbs_to_release(int n, const f1ap_drb_to_release_t *drbs) { F1AP_DRBs_ToBeReleased_List_t list = {0}; for (int i = 0; i < n; i++) { asn1cSequenceAdd(list, F1AP_DRBs_ToBeReleased_ItemIEs_t, itie); itie->id = F1AP_ProtocolIE_ID_id_DRBs_ToBeReleased_Item; itie->criticality = F1AP_Criticality_reject; itie->value.present = F1AP_DRBs_ToBeReleased_ItemIEs__value_PR_DRBs_ToBeReleased_Item; itie->value.choice.DRBs_ToBeReleased_Item.dRBID = drbs[i].id; } return list; } static bool decode_drbs_to_release(const F1AP_DRBs_ToBeReleased_List_t *f1ap, int *n, f1ap_drb_to_release_t **out) { DevAssert(out != NULL); *out = NULL; int count = *n = f1ap->list.count; if (count == 0) return true; f1ap_drb_to_release_t *drbs = *out = calloc_or_fail(count, sizeof(*drbs)); for (int i = 0; i < count; ++i) { const F1AP_DRBs_ToBeReleased_ItemIEs_t *itie = (F1AP_DRBs_ToBeReleased_ItemIEs_t *)f1ap->list.array[i]; _F1_EQ_CHECK_LONG(itie->id, F1AP_ProtocolIE_ID_id_DRBs_ToBeReleased_Item); _F1_EQ_CHECK_INT(itie->value.present, F1AP_DRBs_ToBeReleased_ItemIEs__value_PR_DRBs_ToBeReleased_Item); drbs[i].id = itie->value.choice.DRBs_ToBeReleased_Item.dRBID; } return true; } static f1ap_drb_to_release_t cp_drb_to_release(const f1ap_drb_to_release_t *orig) { f1ap_drb_to_release_t cp = { .id = orig->id, }; return cp; } static bool eq_drb_to_release(const f1ap_drb_to_release_t *a, const f1ap_drb_to_release_t *b) { _F1_EQ_CHECK_INT(a->id, b->id); return true; } static void free_drb_to_release(f1ap_drb_to_release_t *drb) { // nothing to free } /** * @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); } /** * @brief Encode F1 UE context setup response to ASN.1 */ F1AP_F1AP_PDU_t *encode_ue_context_setup_resp(const f1ap_ue_context_setup_resp_t *msg) { F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); /* Message Type */ pdu->present = F1AP_F1AP_PDU_PR_successfulOutcome; asn1cCalloc(pdu->choice.successfulOutcome, tmp); tmp->procedureCode = F1AP_ProcedureCode_id_UEContextSetup; tmp->criticality = F1AP_Criticality_reject; tmp->value.present = F1AP_SuccessfulOutcome__value_PR_UEContextSetupResponse; F1AP_UEContextSetupResponse_t *out = &tmp->value.choice.UEContextSetupResponse; /* mandatory: GNB_CU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupResponseIEs_t, ie1); ie1->id = F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID; ie1->criticality = F1AP_Criticality_reject; ie1->value.present = F1AP_UEContextSetupResponseIEs__value_PR_GNB_CU_UE_F1AP_ID; ie1->value.choice.GNB_CU_UE_F1AP_ID = msg->gNB_CU_ue_id; /* mandatory: GNB_DU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupResponseIEs_t, ie2); ie2->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID; ie2->criticality = F1AP_Criticality_reject; ie2->value.present = F1AP_UEContextSetupResponseIEs__value_PR_GNB_DU_UE_F1AP_ID; ie2->value.choice.GNB_DU_UE_F1AP_ID = msg->gNB_DU_ue_id; /* mandatory: DUtoCURRCInformation */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupResponseIEs_t, ie3); ie3->id = F1AP_ProtocolIE_ID_id_DUtoCURRCInformation; ie3->criticality = F1AP_Criticality_reject; ie3->value.present = F1AP_UEContextSetupResponseIEs__value_PR_DUtoCURRCInformation; ie3->value.choice.DUtoCURRCInformation = encode_du_to_cu_rrc_info(&msg->du_to_cu_rrc_info); /* optional: C-RNTI */ if (msg->crnti) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupResponseIEs_t, ie4); ie4->id = F1AP_ProtocolIE_ID_id_C_RNTI; ie4->criticality = F1AP_Criticality_ignore; ie4->value.present = F1AP_UEContextSetupResponseIEs__value_PR_C_RNTI; ie4->value.choice.C_RNTI = *msg->crnti; } /* optional: DRB Setup List */ if (msg->drbs_len > 0) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupResponseIEs_t, ie7); ie7->id = F1AP_ProtocolIE_ID_id_DRBs_Setup_List; ie7->criticality = F1AP_Criticality_ignore; ie7->value.present = F1AP_UEContextSetupResponseIEs__value_PR_DRBs_Setup_List; ie7->value.choice.DRBs_Setup_List = encode_drbs_setup(msg->drbs_len, msg->drbs); } /* optional: SRB Setup List */ if (msg->srbs_len > 0) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextSetupResponseIEs_t, ie11); ie11->id = F1AP_ProtocolIE_ID_id_SRBs_Setup_List; ie11->criticality = F1AP_Criticality_ignore; ie11->value.present = F1AP_UEContextSetupResponseIEs__value_PR_SRBs_Setup_List; ie11->value.choice.SRBs_Setup_List = encode_srbs_setup(msg->srbs_len, msg->srbs); } return pdu; } /** * @brief Decode F1 UE Context Setup Response */ bool decode_ue_context_setup_resp(const struct F1AP_F1AP_PDU *pdu, f1ap_ue_context_setup_resp_t *out) { DevAssert(out != NULL); memset(out, 0, sizeof(*out)); F1AP_UEContextSetupResponse_t *in = &pdu->choice.successfulOutcome->value.choice.UEContextSetupResponse; F1AP_UEContextSetupResponseIEs_t *ie; F1AP_LIB_FIND_IE(F1AP_UEContextSetupResponseIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true); F1AP_LIB_FIND_IE(F1AP_UEContextSetupResponseIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true); F1AP_LIB_FIND_IE(F1AP_UEContextSetupResponseIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_DUtoCURRCInformation, 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_UEContextSetupResponseIEs__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_UEContextSetupResponseIEs__value_PR_GNB_DU_UE_F1AP_ID); out->gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID; break; case F1AP_ProtocolIE_ID_id_DUtoCURRCInformation: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupResponseIEs__value_PR_DUtoCURRCInformation); _F1_CHECK_EXP(decode_du_to_cu_rrc_info(&out->du_to_cu_rrc_info, &ie->value.choice.DUtoCURRCInformation)); break; case F1AP_ProtocolIE_ID_id_C_RNTI: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupResponseIEs__value_PR_C_RNTI); _F1_MALLOC(out->crnti, ie->value.choice.C_RNTI); break; case F1AP_ProtocolIE_ID_id_DRBs_Setup_List: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupResponseIEs__value_PR_DRBs_Setup_List); _F1_CHECK_EXP(decode_drbs_setup(&ie->value.choice.DRBs_Setup_List, &out->drbs_len, &out->drbs)); break; case F1AP_ProtocolIE_ID_id_SRBs_Setup_List: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextSetupResponseIEs__value_PR_SRBs_Setup_List); _F1_CHECK_EXP(decode_srbs_setup(&ie->value.choice.SRBs_Setup_List, &out->srbs_len, &out->srbs)); break; default: PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); break; } } return true; } /** * @brief F1 UE Context Setup Response deep copy */ f1ap_ue_context_setup_resp_t cp_ue_context_setup_resp(const f1ap_ue_context_setup_resp_t *orig) { f1ap_ue_context_setup_resp_t cp = { .gNB_CU_ue_id = orig->gNB_CU_ue_id, .gNB_DU_ue_id = orig->gNB_DU_ue_id, .du_to_cu_rrc_info = cp_du_to_cu_rrc_info(&orig->du_to_cu_rrc_info), // crnti below .drbs_len = orig->drbs_len, // drbs below .srbs_len = orig->srbs_len, // srbs below }; if (orig->crnti) _F1_MALLOC(cp.crnti, *orig->crnti); 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_setup(&orig->drbs[i]); } 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_setup(&orig->srbs[i]); } return cp; } /** * @brief F1 UE Context Setup Response equality check */ bool eq_ue_context_setup_resp(const f1ap_ue_context_setup_resp_t *a, const f1ap_ue_context_setup_resp_t *b) { _F1_EQ_CHECK_INT(a->gNB_CU_ue_id, b->gNB_CU_ue_id); _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id); _F1_CHECK_EXP(eq_du_to_cu_rrc_info(&a->du_to_cu_rrc_info, &b->du_to_cu_rrc_info)); _F1_EQ_CHECK_OPTIONAL_IE(a, b, crnti, _F1_EQ_CHECK_INT); _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_setup(&a->drbs[i], &b->drbs[i])); _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_setup(&a->srbs[i], &b->srbs[i])); return true; } /** * @brief Free Allocated F1 UE Context Setup Response */ void free_ue_context_setup_resp(f1ap_ue_context_setup_resp_t *resp) { free_du_to_cu_rrc_info(&resp->du_to_cu_rrc_info); free(resp->crnti); for (int i = 0; i < resp->drbs_len; ++i) free_drb_setup(&resp->drbs[i]); free(resp->drbs); for (int i = 0; i < resp->srbs_len; ++i) free_srb_setup(&resp->srbs[i]); free(resp->srbs); } /** * @brief Encode F1 UE context modification request to ASN.1 */ F1AP_F1AP_PDU_t *encode_ue_context_mod_req(const f1ap_ue_context_mod_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_UEContextModification; tmp->criticality = F1AP_Criticality_reject; tmp->value.present = F1AP_InitiatingMessage__value_PR_UEContextModificationRequest; F1AP_UEContextModificationRequest_t *out = &tmp->value.choice.UEContextModificationRequest; /* mandatory: GNB_CU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie1); ie1->id = F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID; ie1->criticality = F1AP_Criticality_reject; ie1->value.present = F1AP_UEContextModificationRequestIEs__value_PR_GNB_CU_UE_F1AP_ID; ie1->value.choice.GNB_CU_UE_F1AP_ID = req->gNB_CU_ue_id; /* mandatory: GNB_DU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie2); ie2->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID; ie2->criticality = F1AP_Criticality_reject; ie2->value.present = F1AP_UEContextModificationRequestIEs__value_PR_GNB_DU_UE_F1AP_ID; ie2->value.choice.GNB_DU_UE_F1AP_ID = req->gNB_DU_ue_id; /* optional: SpCellID/NRCGI */ if (req->plmn) { DevAssert(req->nr_cellid); asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie3); ie3->id = F1AP_ProtocolIE_ID_id_SpCell_ID; ie3->criticality = F1AP_Criticality_ignore; ie3->value.present = F1AP_UEContextModificationRequestIEs__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); } /* optional: ServCellIndex */ if (req->servCellIndex) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie4); ie4->id = F1AP_ProtocolIE_ID_id_ServCellIndex; ie4->criticality = F1AP_Criticality_reject; ie4->value.present = F1AP_UEContextModificationRequestIEs__value_PR_ServCellIndex; ie4->value.choice.ServCellIndex = *req->servCellIndex; } /* optional: CUtoDURRCInformation */ if (req->cu_to_du_rrc_info) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie6); ie6->id = F1AP_ProtocolIE_ID_id_CUtoDURRCInformation; ie6->criticality = F1AP_Criticality_reject; ie6->value.present = F1AP_UEContextModificationRequestIEs__value_PR_CUtoDURRCInformation; ie6->value.choice.CUtoDURRCInformation = encode_cu_to_du_rrc_info(req->cu_to_du_rrc_info); } /* optional: TransmissionActionIndicator */ if (req->transm_action_ind) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie7); ie7->id = F1AP_ProtocolIE_ID_id_TransmissionActionIndicator; ie7->criticality = F1AP_Criticality_ignore; ie7->value.present = F1AP_UEContextModificationRequestIEs__value_PR_TransmissionActionIndicator; ie7->value.choice.TransmissionActionIndicator = *req->transm_action_ind; // Stop is guaranteed to be 0, by restart might be different from 1 static_assert((int)F1AP_TransmissionActionIndicator_restart == (int)TransmActionInd_RESTART, "mismatch of ASN.1 and internal representation\n"); } /* optional: RRCRconfigurationCompleteIndicator */ if (req->reconfig_compl) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie71); ie71->id = F1AP_ProtocolIE_ID_id_RRCReconfigurationCompleteIndicator; ie71->criticality = F1AP_Criticality_ignore; ie71->value.present = F1AP_UEContextModificationRequestIEs__value_PR_RRCReconfigurationCompleteIndicator; ie71->value.choice.RRCReconfigurationCompleteIndicator = *req->reconfig_compl == RRCreconf_success ? F1AP_RRCReconfigurationCompleteIndicator_true : F1AP_RRCReconfigurationCompleteIndicator_failure; } /* optional: RRC container */ if (req->rrc_container) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie81); ie81->id = F1AP_ProtocolIE_ID_id_RRCContainer; ie81->criticality = F1AP_Criticality_ignore; ie81->value.present = F1AP_UEContextModificationRequestIEs__value_PR_RRCContainer; OCTET_STRING_fromBuf(&ie81->value.choice.RRCContainer, (const char *)req->rrc_container->buf, req->rrc_container->len); } /* optional: SRBs_ToBeSetupMod */ if (req->srbs_len > 0) { DevAssert(req->srbs); asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie11); ie11->id = F1AP_ProtocolIE_ID_id_SRBs_ToBeSetupMod_List; ie11->criticality = F1AP_Criticality_reject; ie11->value.present = F1AP_UEContextModificationRequestIEs__value_PR_SRBs_ToBeSetupMod_List; ie11->value.choice.SRBs_ToBeSetupMod_List = encode_srbs_to_setupmod(req->srbs_len, req->srbs); } /* optional: DRBs_ToBeSetupMod */ if (req->drbs_len > 0) { DevAssert(req->drbs); asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie12); ie12->id = F1AP_ProtocolIE_ID_id_DRBs_ToBeSetupMod_List; ie12->criticality = F1AP_Criticality_reject; ie12->value.present = F1AP_UEContextModificationRequestIEs__value_PR_DRBs_ToBeSetupMod_List; ie12->value.choice.DRBs_ToBeSetupMod_List = encode_drbs_to_setupmod(req->drbs_len, req->drbs); } /* optional: DRBs_ToBeReleased */ if (req->drbs_rel_len > 0) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie15); ie15->id = F1AP_ProtocolIE_ID_id_DRBs_ToBeReleased_List; ie15->criticality = F1AP_Criticality_reject; ie15->value.present = F1AP_UEContextModificationRequestIEs__value_PR_DRBs_ToBeReleased_List; ie15->value.choice.DRBs_ToBeReleased_List = encode_drbs_to_release(req->drbs_rel_len, req->drbs_rel); } /* optional: Lower Layer Presence Status Change */ if (req->status) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie17); ie17->id = F1AP_ProtocolIE_ID_id_LowerLayerPresenceStatusChange; ie17->criticality = F1AP_Criticality_ignore; ie17->value.present = F1AP_UEContextModificationRequestIEs__value_PR_LowerLayerPresenceStatusChange; ie17->value.choice.LowerLayerPresenceStatusChange = *req->status == LOWER_LAYERS_SUSPEND ? F1AP_LowerLayerPresenceStatusChange_suspend_lower_layers : F1AP_LowerLayerPresenceStatusChange_resume_lower_layers; } return pdu; } /** * @brief Decode F1 UE Context Modification Request */ bool decode_ue_context_mod_req(const F1AP_F1AP_PDU_t *pdu, f1ap_ue_context_mod_req_t *out) { DevAssert(out != NULL); memset(out, 0, sizeof(*out)); F1AP_UEContextModificationRequest_t *in = &pdu->choice.initiatingMessage->value.choice.UEContextModificationRequest; F1AP_UEContextModificationRequestIEs_t *ie; F1AP_LIB_FIND_IE(F1AP_UEContextModificationRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true); F1AP_LIB_FIND_IE(F1AP_UEContextModificationRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, 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_UEContextModificationRequestIEs__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_UEContextModificationRequestIEs__value_PR_GNB_DU_UE_F1AP_ID); 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_UEContextModificationRequestIEs__value_PR_NRCGI); const F1AP_NRCGI_t *nrcgi = &ie->value.choice.NRCGI; out->plmn = malloc_or_fail(sizeof(*out->plmn)); PLMNID_TO_MCC_MNC(&nrcgi->pLMN_Identity, out->plmn->mcc, out->plmn->mnc, out->plmn->mnc_digit_length); out->nr_cellid = malloc_or_fail(sizeof(*out->nr_cellid)); 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_UEContextModificationRequestIEs__value_PR_ServCellIndex); _F1_MALLOC(out->servCellIndex, ie->value.choice.ServCellIndex); break; case F1AP_ProtocolIE_ID_id_CUtoDURRCInformation: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationRequestIEs__value_PR_CUtoDURRCInformation); out->cu_to_du_rrc_info = calloc(1, sizeof(*out->cu_to_du_rrc_info)); _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_TransmissionActionIndicator: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationRequestIEs__value_PR_TransmissionActionIndicator); { TransmActionInd_t v = ie->value.choice.TransmissionActionIndicator == F1AP_TransmissionActionIndicator_stop ? TransmActionInd_STOP : TransmActionInd_RESTART; _F1_MALLOC(out->transm_action_ind, v); } break; case F1AP_ProtocolIE_ID_id_RRCReconfigurationCompleteIndicator: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationRequestIEs__value_PR_RRCReconfigurationCompleteIndicator); { ReconfigurationCompl_t v = ie->value.choice.RRCReconfigurationCompleteIndicator == F1AP_RRCReconfigurationCompleteIndicator_true ? RRCreconf_success : RRCreconf_failure; _F1_MALLOC(out->reconfig_compl, v); } break; case F1AP_ProtocolIE_ID_id_RRCContainer: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationRequestIEs__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_SRBs_ToBeSetupMod_List: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationRequestIEs__value_PR_SRBs_ToBeSetupMod_List); _F1_CHECK_EXP(decode_srbs_to_setupmod(&ie->value.choice.SRBs_ToBeSetupMod_List, &out->srbs_len, &out->srbs)); break; case F1AP_ProtocolIE_ID_id_DRBs_ToBeSetupMod_List: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationRequestIEs__value_PR_DRBs_ToBeSetupMod_List); _F1_CHECK_EXP(decode_drbs_to_setupmod(&ie->value.choice.DRBs_ToBeSetupMod_List, &out->drbs_len, &out->drbs)); break; case F1AP_ProtocolIE_ID_id_DRBs_ToBeReleased_List: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationRequestIEs__value_PR_DRBs_ToBeReleased_List); _F1_CHECK_EXP(decode_drbs_to_release(&ie->value.choice.DRBs_ToBeReleased_List, &out->drbs_rel_len, &out->drbs_rel)); break; case F1AP_ProtocolIE_ID_id_LowerLayerPresenceStatusChange: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationRequestIEs__value_PR_LowerLayerPresenceStatusChange); { lower_layer_status_t v = ie->value.choice.LowerLayerPresenceStatusChange == F1AP_LowerLayerPresenceStatusChange_suspend_lower_layers ? LOWER_LAYERS_SUSPEND : LOWER_LAYERS_RESUME; _F1_MALLOC(out->status, v); } break; default: PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); break; } } return true; } /** * @brief F1 UE Context Modification Request deep copy */ f1ap_ue_context_mod_req_t cp_ue_context_mod_req(const f1ap_ue_context_mod_req_t *orig) { f1ap_ue_context_mod_req_t cp = { .gNB_CU_ue_id = orig->gNB_CU_ue_id, .gNB_DU_ue_id = orig->gNB_DU_ue_id, // .plmn, .nr_cellid, .servCellIndex below // .cu_to_du_rrc_info below // .transm_action_ind below // .reconfig_compl below // .rrc_container below .srbs_len = orig->srbs_len, // .srbs below .drbs_len = orig->drbs_len, // .drbs below // .status below }; if (orig->plmn) _F1_MALLOC(cp.plmn, *orig->plmn); if (orig->nr_cellid) _F1_MALLOC(cp.nr_cellid, *orig->nr_cellid); if (orig->servCellIndex) _F1_MALLOC(cp.servCellIndex, *orig->servCellIndex); if (orig->cu_to_du_rrc_info) _F1_MALLOC(cp.cu_to_du_rrc_info, cp_cu_to_du_rrc_info(orig->cu_to_du_rrc_info)); if (orig->transm_action_ind) _F1_MALLOC(cp.transm_action_ind, *orig->transm_action_ind); if (orig->reconfig_compl) _F1_MALLOC(cp.reconfig_compl, *orig->reconfig_compl); CP_OPT_BYTE_ARRAY(cp.rrc_container, orig->rrc_container); if (orig->srbs_len > 0) { DevAssert(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) { DevAssert(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]); } if (orig->drbs_rel_len > 0) { DevAssert(orig->drbs_rel); cp.drbs_rel = calloc_or_fail(orig->drbs_rel_len, sizeof(*cp.drbs_rel)); cp.drbs_rel_len = orig->drbs_rel_len; for (int i = 0; i < cp.drbs_rel_len; ++i) cp.drbs_rel[i] = cp_drb_to_release(&orig->drbs_rel[i]); } if (orig->status) _F1_MALLOC(cp.status, *orig->status); return cp; } /** * @brief F1 UE Context Modification Request equality check */ bool eq_ue_context_mod_req(const f1ap_ue_context_mod_req_t *a, const f1ap_ue_context_mod_req_t *b) { _F1_EQ_CHECK_INT(a->gNB_CU_ue_id, b->gNB_CU_ue_id); _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id); _F1_CHECK_EXP((a->plmn && b->plmn) || (!a->plmn && !b->plmn)); _F1_CHECK_EXP(!a->plmn || eq_f1ap_plmn(a->plmn, b->plmn)); _F1_EQ_CHECK_OPTIONAL_IE(a, b, nr_cellid, _F1_EQ_CHECK_LONG); _F1_EQ_CHECK_OPTIONAL_IE(a, b, servCellIndex, _F1_EQ_CHECK_INT); _F1_CHECK_EXP((a->cu_to_du_rrc_info && b->cu_to_du_rrc_info) || (!a->cu_to_du_rrc_info && !b->cu_to_du_rrc_info)); _F1_CHECK_EXP(!a->cu_to_du_rrc_info || eq_cu_to_du_rrc_info(a->cu_to_du_rrc_info, b->cu_to_du_rrc_info)); _F1_EQ_CHECK_OPTIONAL_IE(a, b, transm_action_ind, _F1_EQ_CHECK_INT); _F1_EQ_CHECK_OPTIONAL_IE(a, b, reconfig_compl, _F1_EQ_CHECK_INT); _F1_EQ_CHECK_OPTIONAL_IE(a, b, rrc_container, eq_ba); _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_INT(a->drbs_rel_len, b->drbs_rel_len); _F1_CHECK_EXP(a->drbs_rel_len == 0 || (a->drbs_rel_len && b->drbs_rel_len)); for (int i = 0; i < a->drbs_rel_len; ++i) _F1_CHECK_EXP(eq_drb_to_release(&a->drbs_rel[i], &b->drbs_rel[i])); _F1_EQ_CHECK_OPTIONAL_IE(a, b, status, _F1_EQ_CHECK_INT); return true; } /** * @brief Free Allocated F1 UE Context Modification Request */ void free_ue_context_mod_req(f1ap_ue_context_mod_req_t *req) { free(req->plmn); free(req->nr_cellid); free(req->servCellIndex); if (req->cu_to_du_rrc_info) free_cu_to_du_rrc_info(req->cu_to_du_rrc_info); free(req->cu_to_du_rrc_info); free(req->transm_action_ind); free(req->reconfig_compl); FREE_OPT_BYTE_ARRAY(req->rrc_container); 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); for (int i = 0; i < req->drbs_rel_len; ++i) free_drb_to_release(&req->drbs_rel[i]); free(req->drbs_rel); free(req->status); } /** * @brief Encode F1 UE context modification response to ASN.1 */ F1AP_F1AP_PDU_t *encode_ue_context_mod_resp(const f1ap_ue_context_mod_resp_t *msg) { F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); /* Message Type */ pdu->present = F1AP_F1AP_PDU_PR_successfulOutcome; asn1cCalloc(pdu->choice.successfulOutcome, tmp); tmp->procedureCode = F1AP_ProcedureCode_id_UEContextModification; tmp->criticality = F1AP_Criticality_reject; tmp->value.present = F1AP_SuccessfulOutcome__value_PR_UEContextModificationResponse; F1AP_UEContextModificationResponse_t *out = &tmp->value.choice.UEContextModificationResponse; /* mandatory: GNB_CU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationResponseIEs_t, ie1); ie1->id = F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID; ie1->criticality = F1AP_Criticality_reject; ie1->value.present = F1AP_UEContextModificationResponseIEs__value_PR_GNB_CU_UE_F1AP_ID; ie1->value.choice.GNB_CU_UE_F1AP_ID = msg->gNB_CU_ue_id; /* mandatory: GNB_DU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationResponseIEs_t, ie2); ie2->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID; ie2->criticality = F1AP_Criticality_reject; ie2->value.present = F1AP_UEContextModificationResponseIEs__value_PR_GNB_DU_UE_F1AP_ID; ie2->value.choice.GNB_DU_UE_F1AP_ID = msg->gNB_DU_ue_id; /* optional: DUtoCURRCInformation */ if (msg->du_to_cu_rrc_info != NULL) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationResponseIEs_t, ie4); ie4->id = F1AP_ProtocolIE_ID_id_DUtoCURRCInformation; ie4->criticality = F1AP_Criticality_reject; ie4->value.present = F1AP_UEContextModificationResponseIEs__value_PR_DUtoCURRCInformation; ie4->value.choice.DUtoCURRCInformation = encode_du_to_cu_rrc_info(msg->du_to_cu_rrc_info); } /* optional: DRBs_Setup_List */ if (msg->drbs_len > 0) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationResponseIEs_t, ie5); ie5->id = F1AP_ProtocolIE_ID_id_DRBs_SetupMod_List; ie5->criticality = F1AP_Criticality_ignore; ie5->value.present = F1AP_UEContextModificationResponseIEs__value_PR_DRBs_SetupMod_List; ie5->value.choice.DRBs_SetupMod_List = encode_drbs_setupmod(msg->drbs_len, msg->drbs); } if (msg->srbs_len > 0) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationResponseIEs_t, ie12); ie12->id = F1AP_ProtocolIE_ID_id_SRBs_SetupMod_List; ie12->criticality = F1AP_Criticality_ignore; ie12->value.present = F1AP_UEContextModificationResponseIEs__value_PR_SRBs_SetupMod_List; ie12->value.choice.SRBs_SetupMod_List = encode_srbs_setupmod(msg->srbs_len, msg->srbs); } return pdu; } /** * @brief Decode F1 UE Context modification Response */ bool decode_ue_context_mod_resp(const struct F1AP_F1AP_PDU *pdu, f1ap_ue_context_mod_resp_t *out) { DevAssert(out != NULL); memset(out, 0, sizeof(*out)); F1AP_UEContextModificationResponse_t *in = &pdu->choice.successfulOutcome->value.choice.UEContextModificationResponse; F1AP_UEContextModificationResponseIEs_t *ie; F1AP_LIB_FIND_IE(F1AP_UEContextModificationResponseIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true); F1AP_LIB_FIND_IE(F1AP_UEContextModificationResponseIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, 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_UEContextModificationResponseIEs__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_UEContextModificationResponseIEs__value_PR_GNB_DU_UE_F1AP_ID); out->gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID; break; case F1AP_ProtocolIE_ID_id_DUtoCURRCInformation: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationResponseIEs__value_PR_DUtoCURRCInformation); out->du_to_cu_rrc_info = calloc(1, sizeof(*out->du_to_cu_rrc_info)); _F1_CHECK_EXP(decode_du_to_cu_rrc_info(out->du_to_cu_rrc_info, &ie->value.choice.DUtoCURRCInformation)); break; case F1AP_ProtocolIE_ID_id_DRBs_SetupMod_List: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationResponseIEs__value_PR_DRBs_SetupMod_List); _F1_CHECK_EXP(decode_drbs_setupmod(&ie->value.choice.DRBs_SetupMod_List, &out->drbs_len, &out->drbs)); break; case F1AP_ProtocolIE_ID_id_SRBs_SetupMod_List: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationResponseIEs__value_PR_SRBs_SetupMod_List); _F1_CHECK_EXP(decode_srbs_setupmod(&ie->value.choice.SRBs_SetupMod_List, &out->srbs_len, &out->srbs)); break; default: PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); break; } } return true; } /** * @brief F1 UE Context modification Response deep copy */ f1ap_ue_context_mod_resp_t cp_ue_context_mod_resp(const f1ap_ue_context_mod_resp_t *orig) { f1ap_ue_context_mod_resp_t cp = { .gNB_CU_ue_id = orig->gNB_CU_ue_id, .gNB_DU_ue_id = orig->gNB_DU_ue_id, // du_to_cu_rrc_info .drbs_len = orig->drbs_len, // drbs below .srbs_len = orig->srbs_len, // srbs below }; if (orig->du_to_cu_rrc_info) { f1ap_du_to_cu_rrc_info_t du2cu = cp_du_to_cu_rrc_info(orig->du_to_cu_rrc_info); _F1_MALLOC(cp.du_to_cu_rrc_info, du2cu); } if (orig->drbs_len > 0) { DevAssert(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_setup(&orig->drbs[i]); } if (orig->srbs_len > 0) { DevAssert(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_setup(&orig->srbs[i]); } return cp; } /** * @brief F1 UE Context modification Response equality check */ bool eq_ue_context_mod_resp(const f1ap_ue_context_mod_resp_t *a, const f1ap_ue_context_mod_resp_t *b) { _F1_EQ_CHECK_INT(a->gNB_CU_ue_id, b->gNB_CU_ue_id); _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id); //_F1_EQ_CHECK_OPTIONAL_IE(a, b, du_to_cu_rrc_info, eq_du_to_cu_rrc_info); _F1_CHECK_EXP((a->du_to_cu_rrc_info && b->du_to_cu_rrc_info) || (!a->du_to_cu_rrc_info && !b->du_to_cu_rrc_info)); _F1_CHECK_EXP(!a->du_to_cu_rrc_info || eq_du_to_cu_rrc_info(a->du_to_cu_rrc_info, b->du_to_cu_rrc_info)); _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_setup(&a->drbs[i], &b->drbs[i])); _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_setup(&a->srbs[i], &b->srbs[i])); return true; } /** * @brief Free Allocated F1 UE Context modification Response */ void free_ue_context_mod_resp(f1ap_ue_context_mod_resp_t *resp) { if (resp->du_to_cu_rrc_info) free_du_to_cu_rrc_info(resp->du_to_cu_rrc_info); free(resp->du_to_cu_rrc_info); for (int i = 0; i < resp->drbs_len; ++i) free_drb_setup(&resp->drbs[i]); free(resp->drbs); for (int i = 0; i < resp->srbs_len; ++i) free_srb_setup(&resp->srbs[i]); free(resp->srbs); } /* * @brief Encode F1 UE context release request to ASN.1 */ struct F1AP_F1AP_PDU *encode_ue_context_rel_req(const f1ap_ue_context_rel_req_t *msg) { 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_UEContextReleaseRequest; tmp->criticality = F1AP_Criticality_reject; tmp->value.present = F1AP_InitiatingMessage__value_PR_UEContextReleaseRequest; F1AP_UEContextReleaseRequest_t *out = &tmp->value.choice.UEContextReleaseRequest; /* mandatory: GNB_CU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseRequestIEs_t, ie1); ie1->id = F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID; ie1->criticality = F1AP_Criticality_reject; ie1->value.present = F1AP_UEContextReleaseRequestIEs__value_PR_GNB_CU_UE_F1AP_ID; ie1->value.choice.GNB_CU_UE_F1AP_ID = msg->gNB_CU_ue_id; /* mandatory: GNB_DU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseRequestIEs_t, ie2); ie2->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID; ie2->criticality = F1AP_Criticality_reject; ie2->value.present = F1AP_UEContextReleaseRequestIEs__value_PR_GNB_DU_UE_F1AP_ID; ie2->value.choice.GNB_DU_UE_F1AP_ID = msg->gNB_DU_ue_id; /* mandatory: Cause */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseRequestIEs_t, ie3); ie3->id = F1AP_ProtocolIE_ID_id_Cause; ie3->criticality = F1AP_Criticality_ignore; ie3->value.present = F1AP_UEContextReleaseRequestIEs__value_PR_Cause; ie3->value.choice.Cause = encode_f1ap_cause(msg->cause, msg->cause_value); return pdu; } /** * @brief Decode F1 UE Context Release Request */ bool decode_ue_context_rel_req(const struct F1AP_F1AP_PDU *pdu, f1ap_ue_context_rel_req_t *out) { DevAssert(out != NULL); memset(out, 0, sizeof(*out)); F1AP_UEContextReleaseRequest_t *in = &pdu->choice.initiatingMessage->value.choice.UEContextReleaseRequest; F1AP_UEContextReleaseRequestIEs_t *ie; F1AP_LIB_FIND_IE(F1AP_UEContextReleaseRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true); F1AP_LIB_FIND_IE(F1AP_UEContextReleaseRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true); F1AP_LIB_FIND_IE(F1AP_UEContextReleaseRequestIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_Cause, 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_UEContextReleaseRequestIEs__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_UEContextReleaseRequestIEs__value_PR_GNB_DU_UE_F1AP_ID); out->gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID; break; case F1AP_ProtocolIE_ID_id_Cause: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextReleaseRequestIEs__value_PR_Cause); _F1_CHECK_EXP(decode_f1ap_cause(ie->value.choice.Cause, &out->cause, &out->cause_value)); break; default: PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); break; } } return true; } /** * @brief F1 UE Context Release Request deep copy */ f1ap_ue_context_rel_req_t cp_ue_context_rel_req(const f1ap_ue_context_rel_req_t *orig) { f1ap_ue_context_rel_req_t cp = { .gNB_CU_ue_id = orig->gNB_CU_ue_id, .gNB_DU_ue_id = orig->gNB_DU_ue_id, .cause = orig->cause, .cause_value = orig->cause_value, }; return cp; } /** * @brief F1 UE Context Release Request equality check */ bool eq_ue_context_rel_req(const f1ap_ue_context_rel_req_t *a, const f1ap_ue_context_rel_req_t *b) { _F1_EQ_CHECK_INT(a->gNB_CU_ue_id, b->gNB_CU_ue_id); _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id); _F1_EQ_CHECK_INT(a->cause, b->cause); _F1_EQ_CHECK_LONG(a->cause_value, b->cause_value); return true; } /** * @brief Free Allocated F1 UE Context Release Request */ void free_ue_context_rel_req(f1ap_ue_context_rel_req_t *req) { // nothing to free } /* * @brief Encode F1 UE context release command to ASN.1 */ struct F1AP_F1AP_PDU *encode_ue_context_rel_cmd(const f1ap_ue_context_rel_cmd_t *cmd) { 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_UEContextRelease; tmp->criticality = F1AP_Criticality_reject; tmp->value.present = F1AP_InitiatingMessage__value_PR_UEContextReleaseCommand; F1AP_UEContextReleaseCommand_t *out = &tmp->value.choice.UEContextReleaseCommand; /* mandatory: GNB_CU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseCommandIEs_t, ie1); ie1->id = F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID; ie1->criticality = F1AP_Criticality_reject; ie1->value.present = F1AP_UEContextReleaseCommandIEs__value_PR_GNB_CU_UE_F1AP_ID; ie1->value.choice.GNB_CU_UE_F1AP_ID = cmd->gNB_CU_ue_id; /* mandatory: GNB_DU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseCommandIEs_t, ie2); ie2->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID; ie2->criticality = F1AP_Criticality_reject; ie2->value.present = F1AP_UEContextReleaseCommandIEs__value_PR_GNB_DU_UE_F1AP_ID; ie2->value.choice.GNB_DU_UE_F1AP_ID = cmd->gNB_DU_ue_id; /* mandatory: Cause */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseCommandIEs_t, ie3); ie3->id = F1AP_ProtocolIE_ID_id_Cause; ie3->criticality = F1AP_Criticality_ignore; ie3->value.present = F1AP_UEContextReleaseCommandIEs__value_PR_Cause; ie3->value.choice.Cause = encode_f1ap_cause(cmd->cause, cmd->cause_value); /* optional: RRC Container */ if (cmd->rrc_container != NULL) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseCommandIEs_t, ie4); ie4->id = F1AP_ProtocolIE_ID_id_RRCContainer; ie4->criticality = F1AP_Criticality_ignore; ie4->value.present = F1AP_UEContextReleaseCommandIEs__value_PR_RRCContainer; const byte_array_t *ba = cmd->rrc_container; OCTET_STRING_fromBuf(&ie4->value.choice.RRCContainer, (const char *)ba->buf, ba->len); // conditionally have SRBID if RRC Container AssertFatal(cmd->srb_id != NULL, "UEContextReleaseCommand SRB-ID C-ifRRCContainer violated\n"); asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseCommandIEs_t, ie5); ie5->id = F1AP_ProtocolIE_ID_id_SRBID; ie5->criticality = F1AP_Criticality_ignore; ie5->value.present = F1AP_UEContextReleaseCommandIEs__value_PR_SRBID; ie5->value.choice.SRBID = *cmd->srb_id; } if (cmd->old_gNB_DU_ue_id != NULL) { asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseCommandIEs_t, ie5); ie5->id = F1AP_ProtocolIE_ID_id_oldgNB_DU_UE_F1AP_ID; ie5->criticality = F1AP_Criticality_ignore; ie5->value.present = F1AP_UEContextReleaseCommandIEs__value_PR_GNB_DU_UE_F1AP_ID_1; ie5->value.choice.GNB_DU_UE_F1AP_ID_1 = *cmd->old_gNB_DU_ue_id; } return pdu; } /** * @brief Decode F1 UE Context Release Command */ bool decode_ue_context_rel_cmd(const struct F1AP_F1AP_PDU *pdu, f1ap_ue_context_rel_cmd_t *out) { DevAssert(out != NULL); memset(out, 0, sizeof(*out)); F1AP_UEContextReleaseCommand_t *in = &pdu->choice.initiatingMessage->value.choice.UEContextReleaseCommand; F1AP_UEContextReleaseCommandIEs_t *ie; F1AP_LIB_FIND_IE(F1AP_UEContextReleaseCommandIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true); F1AP_LIB_FIND_IE(F1AP_UEContextReleaseCommandIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true); F1AP_LIB_FIND_IE(F1AP_UEContextReleaseCommandIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_Cause, 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_UEContextReleaseCommandIEs__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_UEContextReleaseCommandIEs__value_PR_GNB_DU_UE_F1AP_ID); out->gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID; break; case F1AP_ProtocolIE_ID_id_Cause: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextReleaseCommandIEs__value_PR_Cause); _F1_CHECK_EXP(decode_f1ap_cause(ie->value.choice.Cause, &out->cause, &out->cause_value)); break; case F1AP_ProtocolIE_ID_id_RRCContainer: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextReleaseCommandIEs__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_SRBID: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextReleaseCommandIEs__value_PR_SRBID); { // SRB-ID F1AP_UEContextReleaseCommandIEs_t *check_ie; F1AP_LIB_FIND_IE(F1AP_UEContextReleaseCommandIEs_t, check_ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_RRCContainer, true); } _F1_MALLOC(out->srb_id, ie->value.choice.SRBID); break; case F1AP_ProtocolIE_ID_id_oldgNB_DU_UE_F1AP_ID: _F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextReleaseCommandIEs__value_PR_GNB_DU_UE_F1AP_ID_1); _F1_MALLOC(out->old_gNB_DU_ue_id, ie->value.choice.GNB_DU_UE_F1AP_ID_1); break; default: PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); break; } } return true; } /** * @brief F1 UE Context Release Command deep copy */ f1ap_ue_context_rel_cmd_t cp_ue_context_rel_cmd(const f1ap_ue_context_rel_cmd_t *orig) { f1ap_ue_context_rel_cmd_t cp = { .gNB_CU_ue_id = orig->gNB_CU_ue_id, .gNB_DU_ue_id = orig->gNB_DU_ue_id, .cause = orig->cause, .cause_value = orig->cause_value, }; CP_OPT_BYTE_ARRAY(cp.rrc_container, orig->rrc_container); if (orig->rrc_container) { AssertFatal(orig->srb_id != NULL, "UEContextReleaseCommand SRB-ID C-ifRRCContainer violated\n"); _F1_MALLOC(cp.srb_id, *orig->srb_id); } if (orig->old_gNB_DU_ue_id) _F1_MALLOC(cp.old_gNB_DU_ue_id, *orig->old_gNB_DU_ue_id); return cp; } /** * @brief F1 UE Context Release Command equality check */ bool eq_ue_context_rel_cmd(const f1ap_ue_context_rel_cmd_t *a, const f1ap_ue_context_rel_cmd_t *b) { _F1_EQ_CHECK_INT(a->gNB_CU_ue_id, b->gNB_CU_ue_id); _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id); _F1_EQ_CHECK_INT(a->cause, b->cause); _F1_EQ_CHECK_LONG(a->cause_value, b->cause_value); _F1_EQ_CHECK_OPTIONAL_IE(a, b, rrc_container, eq_ba); _F1_EQ_CHECK_OPTIONAL_IE(a, b, srb_id, _F1_EQ_CHECK_INT); _F1_EQ_CHECK_OPTIONAL_IE(a, b, old_gNB_DU_ue_id, _F1_EQ_CHECK_INT); return true; } /** * @brief Free Allocated F1 UE Context Release Command */ void free_ue_context_rel_cmd(f1ap_ue_context_rel_cmd_t *cmd) { FREE_OPT_BYTE_ARRAY(cmd->rrc_container); free(cmd->srb_id); free(cmd->old_gNB_DU_ue_id); } /* * @brief Encode F1 UE context release complete to ASN.1 */ struct F1AP_F1AP_PDU *encode_ue_context_rel_cplt(const f1ap_ue_context_rel_cplt_t *cplt) { F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); /* Message Type */ pdu->present = F1AP_F1AP_PDU_PR_successfulOutcome; asn1cCalloc(pdu->choice.successfulOutcome, tmp); tmp->procedureCode = F1AP_ProcedureCode_id_UEContextRelease; tmp->criticality = F1AP_Criticality_reject; tmp->value.present = F1AP_SuccessfulOutcome__value_PR_UEContextReleaseComplete; F1AP_UEContextReleaseComplete_t *out = &tmp->value.choice.UEContextReleaseComplete; /* mandatory: GNB_CU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseCompleteIEs_t, ie1); ie1->id = F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID; ie1->criticality = F1AP_Criticality_reject; ie1->value.present = F1AP_UEContextReleaseCompleteIEs__value_PR_GNB_CU_UE_F1AP_ID; ie1->value.choice.GNB_CU_UE_F1AP_ID = cplt->gNB_CU_ue_id; /* mandatory: GNB_DU_UE_F1AP_ID */ asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextReleaseCompleteIEs_t, ie2); ie2->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID; ie2->criticality = F1AP_Criticality_reject; ie2->value.present = F1AP_UEContextReleaseCompleteIEs__value_PR_GNB_DU_UE_F1AP_ID; ie2->value.choice.GNB_DU_UE_F1AP_ID = cplt->gNB_DU_ue_id; return pdu; } /** * @brief Decode F1 UE Context Release Complete */ bool decode_ue_context_rel_cplt(const struct F1AP_F1AP_PDU *pdu, f1ap_ue_context_rel_cplt_t *out) { DevAssert(out != NULL); memset(out, 0, sizeof(*out)); F1AP_UEContextReleaseComplete_t *in = &pdu->choice.successfulOutcome->value.choice.UEContextReleaseComplete; F1AP_UEContextReleaseCompleteIEs_t *ie; F1AP_LIB_FIND_IE(F1AP_UEContextReleaseCompleteIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true); F1AP_LIB_FIND_IE(F1AP_UEContextReleaseCompleteIEs_t, ie, &in->protocolIEs.list, F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, 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_UEContextReleaseCompleteIEs__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_UEContextReleaseCompleteIEs__value_PR_GNB_DU_UE_F1AP_ID); out->gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID; break; default: PRINT_ERROR("F1AP_ProtocolIE_ID_id %ld unknown, skipping\n", ie->id); break; } } return true; } /** * @brief F1 UE Context Release Complete deep copy */ f1ap_ue_context_rel_cplt_t cp_ue_context_rel_cplt(const f1ap_ue_context_rel_cplt_t *orig) { f1ap_ue_context_rel_cplt_t cp = { .gNB_CU_ue_id = orig->gNB_CU_ue_id, .gNB_DU_ue_id = orig->gNB_DU_ue_id, }; return cp; } /** * @brief F1 UE Context Release Complete equality check */ bool eq_ue_context_rel_cplt(const f1ap_ue_context_rel_cplt_t *a, const f1ap_ue_context_rel_cplt_t *b) { _F1_EQ_CHECK_INT(a->gNB_CU_ue_id, b->gNB_CU_ue_id); _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id); return true; } /** * @brief Free Allocated F1 UE Context Release Complete */ void free_ue_context_rel_cplt(f1ap_ue_context_rel_cplt_t *cplt) { // nothing to free }