Add F1AP UE context modification request message enc/dec + test

See commit "Add F1AP UE context setup request message enc/dec + test"
for more details on the approach taken.
This commit is contained in:
Robert Schmidt
2025-06-28 12:19:57 +02:00
parent da915d21e0
commit 807e44d604
4 changed files with 719 additions and 0 deletions
+37
View File
@@ -485,6 +485,11 @@ typedef enum TransmActionInd_e {
TransmActionInd_RESTART,
} TransmActionInd_t;
typedef enum lower_layer_status_e {
LOWER_LAYERS_SUSPEND,
LOWER_LAYERS_RESUME,
} lower_layer_status_t;
typedef struct f1ap_srb_to_setup_t {
int id;
} f1ap_srb_to_setup_t;
@@ -555,6 +560,10 @@ typedef struct f1ap_drb_to_setup_t {
f1ap_pdcp_sn_len_t *ul_pdcp_sn_len;
} f1ap_drb_to_setup_t;
typedef struct f1ap_drb_to_release_t {
int id;
} f1ap_drb_to_release_t;
typedef struct f1ap_ue_context_setup_req_s {
uint32_t gNB_CU_ue_id;
uint32_t *gNB_DU_ue_id;
@@ -576,6 +585,34 @@ typedef struct f1ap_ue_context_setup_req_s {
uint64_t *gnb_du_ue_agg_mbr_ul; // C-ifDRBSetup
} f1ap_ue_context_setup_req_t;
typedef struct f1ap_ue_context_mod_req_t {
uint32_t gNB_CU_ue_id;
uint32_t gNB_DU_ue_id;
plmn_id_t *plmn;
uint64_t *nr_cellid;
uint8_t *servCellIndex;
f1ap_cu_to_du_rrc_info_t *cu_to_du_rrc_info;
TransmActionInd_t *transm_action_ind;
ReconfigurationCompl_t *reconfig_compl;
byte_array_t *rrc_container;
int srbs_len;
f1ap_srb_to_setup_t *srbs; // reuse the same type as for setup; there is a
// list for SRB modification
int drbs_len;
f1ap_drb_to_setup_t *drbs; // as for SRBs
int drbs_rel_len;
f1ap_drb_to_release_t *drbs_rel;
lower_layer_status_t *status;
} f1ap_ue_context_mod_req_t;
typedef struct f1ap_ue_context_setup_s {
uint32_t gNB_CU_ue_id;
uint32_t gNB_DU_ue_id;
+565
View File
@@ -153,6 +153,22 @@ static F1AP_SRBs_ToBeSetup_List_t encode_srbs_to_setup(int n, const f1ap_srb_to_
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)
@@ -174,6 +190,28 @@ static bool decode_srbs_to_setup(const F1AP_SRBs_ToBeSetup_List_t *f1ap, int *n,
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 = {
@@ -508,6 +546,64 @@ static F1AP_DRBs_ToBeSetup_List_t encode_drbs_to_setup(int n, const f1ap_drb_to_
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)
@@ -571,6 +667,71 @@ static bool decode_drbs_to_setup(const F1AP_DRBs_ToBeSetup_List_t *f1ap, int *n,
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 = {
@@ -623,6 +784,57 @@ static void free_drb_to_setup(f1ap_drb_to_setup_t *drb)
free(drb->ul_pdcp_sn_len);
}
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
*/
@@ -874,3 +1086,356 @@ void free_ue_context_setup_req(f1ap_ue_context_setup_req_t *req)
FREE_OPT_BYTE_ARRAY(req->rrc_container);
free(req->gnb_du_ue_agg_mbr_ul);
}
/**
* @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);
}
+6
View File
@@ -33,4 +33,10 @@ f1ap_ue_context_setup_req_t cp_ue_context_setup_req(const f1ap_ue_context_setup_
bool eq_ue_context_setup_req(const f1ap_ue_context_setup_req_t *a, const f1ap_ue_context_setup_req_t *b);
void free_ue_context_setup_req(f1ap_ue_context_setup_req_t *req);
struct F1AP_F1AP_PDU *encode_ue_context_mod_req(const f1ap_ue_context_mod_req_t *msg);
bool decode_ue_context_mod_req(const struct F1AP_F1AP_PDU *pdu, f1ap_ue_context_mod_req_t *out);
f1ap_ue_context_mod_req_t cp_ue_context_mod_req(const f1ap_ue_context_mod_req_t *orig);
bool eq_ue_context_mod_req(const f1ap_ue_context_mod_req_t *a, const f1ap_ue_context_mod_req_t *b);
void free_ue_context_mod_req(f1ap_ue_context_mod_req_t *req);
#endif /* F1AP_UE_CONTEXT_SETUP_REQ_H_ */
+111
View File
@@ -886,6 +886,115 @@ static void test_f1ap_ue_context_setup_request_simple()
}
static void test_f1ap_ue_context_modification_request()
{
f1ap_ue_context_mod_req_t orig = {
.gNB_CU_ue_id = 1111,
.gNB_DU_ue_id = 2222,
// rest below
};
plmn_id_t plmn = { .mcc = 999, .mnc = 101, .mnc_digit_length = 3 };
_F1_MALLOC(orig.plmn, plmn);
_F1_MALLOC(orig.nr_cellid, 1532);
_F1_MALLOC(orig.servCellIndex, 3);
f1ap_cu_to_du_rrc_info_t cu2du = {
.cg_configinfo = get_malloced_test_ba("Modified"),
.ue_cap = get_malloced_test_ba("UE forgot to send earlier"),
.meas_config = get_malloced_test_ba("with updates from another cell"),
.ho_prep_info = get_malloced_test_ba("prepare to ho soon!"),
};
_F1_MALLOC(orig.cu_to_du_rrc_info, cu2du);
orig.rrc_container = get_malloced_test_ba("important RRC message!!");
orig.srbs_len = 1;
orig.srbs = calloc_or_fail(orig.srbs_len, sizeof(*orig.srbs));
orig.srbs[0].id = 1;
orig.drbs_len = 1;
orig.drbs = calloc_or_fail(orig.drbs_len, sizeof(*orig.drbs));
f1ap_drb_to_setup_t *drb1 = &orig.drbs[0];
drb1->id = 4;
drb1->qos_choice = F1AP_QOS_CHOICE_NR;
f1ap_arp_t arp = { 2, SHALL_NOT_TRIGGER_PREEMPTION, NOT_PREEMPTABLE, };
drb1->nr.drb_qos.qos_type = NON_DYNAMIC;
drb1->nr.drb_qos.nondyn.fiveQI = 8;
drb1->nr.drb_qos.arp = arp;
drb1->nr.nssai = (nssai_t) {.sst = 2, .sd = 0xffffff};
drb1->nr.flows_len = 1;
f1ap_drb_flows_mapped_t *flow = drb1->nr.flows = calloc_or_fail(drb1->nr.flows_len, sizeof(*flow));
flow->qfi = 2;
flow->param.qos_type = NON_DYNAMIC;
flow->param.nondyn.fiveQI = 9;
flow->param.arp = arp;
drb1->up_ul_tnl_len = 1;
inet_pton(AF_INET, "8.8.8.8", &drb1->up_ul_tnl[0].tl_address);
drb1->up_ul_tnl[0].teid = 0x9876541;
drb1->rlc_mode = F1AP_RLC_MODE_AM;
_F1_MALLOC(drb1->dl_pdcp_sn_len, F1AP_PDCP_SN_12B);
_F1_MALLOC(drb1->ul_pdcp_sn_len, F1AP_PDCP_SN_18B);
orig.drbs_rel_len = 3;
orig.drbs_rel = calloc_or_fail(orig.drbs_rel_len, sizeof(*orig.drbs_rel));
orig.drbs_rel[0].id = 13;
orig.drbs_rel[1].id = 4;
orig.drbs_rel[2].id = 19;
_F1_MALLOC(orig.status, LOWER_LAYERS_RESUME);
F1AP_F1AP_PDU_t *f1enc = encode_ue_context_mod_req(&orig);
F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc);
f1ap_msg_free(f1enc);
f1ap_ue_context_mod_req_t decoded = {0};
bool ret = decode_ue_context_mod_req(f1dec, &decoded);
AssertFatal(ret, "decode_ue_context_mod_req(): could not decode message\n");
f1ap_msg_free(f1dec);
ret = eq_ue_context_mod_req(&orig, &decoded);
AssertFatal(ret, "eq_ue_context_mod_req(): decoded message doesn't match\n");
free_ue_context_mod_req(&decoded);
f1ap_ue_context_mod_req_t cp = cp_ue_context_mod_req(&orig);
ret = eq_ue_context_mod_req(&orig, &cp);
AssertFatal(ret, "eq_ue_context_mod_req(): copied message doesn't match\n");
free_ue_context_mod_req(&orig);
free_ue_context_mod_req(&cp);
printf("%s() successful\n", __func__);
}
static void test_f1ap_ue_context_modification_request_simple()
{
f1ap_ue_context_mod_req_t orig = {
.gNB_CU_ue_id = 1111,
.gNB_DU_ue_id = 2222,
// rest is optional and intentionally left out
};
F1AP_F1AP_PDU_t *f1enc = encode_ue_context_mod_req(&orig);
F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc);
f1ap_msg_free(f1enc);
f1ap_ue_context_mod_req_t decoded = {0};
bool ret = decode_ue_context_mod_req(f1dec, &decoded);
AssertFatal(ret, "decode_ue_context_mod_req(): could not decode message\n");
f1ap_msg_free(f1dec);
ret = eq_ue_context_mod_req(&orig, &decoded);
AssertFatal(ret, "eq_ue_context_mod_req(): decoded message doesn't match\n");
free_ue_context_mod_req(&decoded);
f1ap_ue_context_mod_req_t cp = cp_ue_context_mod_req(&orig);
ret = eq_ue_context_mod_req(&orig, &cp);
AssertFatal(ret, "eq_ue_context_mod_req(): copied message doesn't match\n");
free_ue_context_mod_req(&orig);
free_ue_context_mod_req(&cp);
printf("%s() successful\n", __func__);
}
int main()
{
test_initial_ul_rrc_message_transfer();
@@ -903,5 +1012,7 @@ int main()
test_f1ap_du_configuration_update_acknowledge();
test_f1ap_ue_context_setup_request();
test_f1ap_ue_context_setup_request_simple();
test_f1ap_ue_context_modification_request();
test_f1ap_ue_context_modification_request_simple();
return 0;
}