From 467015e005c3a469450dbb2bf9e7e5e7b8885535 Mon Sep 17 00:00:00 2001 From: Rakesh BB Date: Sat, 31 Jan 2026 10:16:26 +0000 Subject: [PATCH] XNAP: add encode/decode and unit test for Xn Setup Response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Xn Setup Response message type definitions - Implement encoder and decoder for Xn Setup Response Xn Setup Response (3GPP TS 38.423v16.2.0 §9.1.3.2) - Global NG-RAN Node ID (M) - TAI Support List (M) - Add equality check and memory management helpers - Extend XNAP library unit tests to cover Setup Response --- openair2/COMMON/xnap_messages_types.h | 10 ++ .../XNAP/lib/xnap_gNB_interface_management.c | 159 ++++++++++++++++++ .../XNAP/lib/xnap_gNB_interface_management.h | 4 + openair2/XNAP/lib/xnap_lib_includes.h | 2 +- openair2/XNAP/tests/xnap_lib_test.c | 85 ++++++++++ 5 files changed, 259 insertions(+), 1 deletion(-) diff --git a/openair2/COMMON/xnap_messages_types.h b/openair2/COMMON/xnap_messages_types.h index 1080e5fb1b..fc360d2558 100644 --- a/openair2/COMMON/xnap_messages_types.h +++ b/openair2/COMMON/xnap_messages_types.h @@ -62,4 +62,14 @@ typedef struct { xnap_amf_region_info_t *amf_region_info; } xnap_setup_req_t; +/* 3GPP TS 38.423 9.1.3.2 – Xn Setup Response */ +typedef struct { + // Global NG-RAN Node ID (gNB_id+plmn) (M) + uint32_t gNB_id; + plmn_id_t plmn; + // TAI Support List (M) + uint16_t num_tai; + xnap_tai_support_t *tai_support; +} xnap_setup_resp_t; + #endif /* XNAP_MESSAGES_TYPES_H_ */ diff --git a/openair2/XNAP/lib/xnap_gNB_interface_management.c b/openair2/XNAP/lib/xnap_gNB_interface_management.c index b374580851..409ce7b967 100644 --- a/openair2/XNAP/lib/xnap_gNB_interface_management.c +++ b/openair2/XNAP/lib/xnap_gNB_interface_management.c @@ -244,3 +244,162 @@ void free_xnap_setup_request(const xnap_setup_req_t *msg) free_xnap_tai_support(msg->tai_support, msg->num_tai); free(msg->amf_region_info); } + +/** + * @brief XnAP Setup Response encoding + */ +XNAP_XnAP_PDU_t *encode_xn_setup_response(const xnap_setup_resp_t *resp) +{ + XNAP_XnAP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + + /* PDU type */ + pdu->present = XNAP_XnAP_PDU_PR_successfulOutcome; + asn1cCalloc(pdu->choice.successfulOutcome, succMsg); + succMsg->procedureCode = XNAP_ProcedureCode_id_xnSetup; + succMsg->criticality = XNAP_Criticality_reject; + succMsg->value.present = XNAP_SuccessfulOutcome__value_PR_XnSetupResponse; + + XNAP_XnSetupResponse_t *out = &succMsg->value.choice.XnSetupResponse; + + /* Global NG-RAN Node ID (M) */ + asn1cSequenceAdd(out->protocolIEs.list, XNAP_XnSetupResponse_IEs_t, ie1); + ie1->id = XNAP_ProtocolIE_ID_id_GlobalNG_RAN_node_ID; + ie1->criticality = XNAP_Criticality_reject; + ie1->value.present = XNAP_XnSetupResponse_IEs__value_PR_GlobalNG_RANNode_ID; + + XNAP_GlobalNG_RANNode_ID_t *global = &ie1->value.choice.GlobalNG_RANNode_ID; + global->present = XNAP_GlobalNG_RANNode_ID_PR_gNB; + asn1cCalloc(global->choice.gNB, gnb); + + MCC_MNC_TO_PLMNID(resp->plmn.mcc, resp->plmn.mnc, resp->plmn.mnc_digit_length, &gnb->plmn_id); + gnb->gnb_id.present = XNAP_GNB_ID_Choice_PR_gnb_ID; + MACRO_GNB_ID_TO_BIT_STRING(resp->gNB_id, &gnb->gnb_id.choice.gnb_ID); + + /* TAI Support List (M) */ + asn1cSequenceAdd(out->protocolIEs.list, XNAP_XnSetupResponse_IEs_t, ie2); + ie2->id = XNAP_ProtocolIE_ID_id_TAISupport_list; + ie2->criticality = XNAP_Criticality_reject; + ie2->value.present = XNAP_XnSetupResponse_IEs__value_PR_TAISupport_List; + + for (int i = 0; i < resp->num_tai; i++) { + asn1cSequenceAdd(ie2->value.choice.TAISupport_List.list, XNAP_TAISupport_Item_t, taiItem); + INT24_TO_OCTET_STRING(resp->tai_support[i].tac, &taiItem->tac); + + for (int j = 0; j < resp->tai_support[i].num_plmn; j++) { + const xnap_plmn_support_t *plmn = &resp->tai_support[i].plmn_support[j]; + asn1cSequenceAdd(taiItem->broadcastPLMNs.list, XNAP_BroadcastPLMNinTAISupport_Item_t, plmnItem); + MCC_MNC_TO_PLMNID(plmn->plmn.mcc, plmn->plmn.mnc, plmn->plmn.mnc_digit_length, &plmnItem->plmn_id); + + for (int k = 0; k < plmn->num_nssai; k++) { + asn1cSequenceAdd(plmnItem->tAISliceSupport_List.list, XNAP_S_NSSAI_t, nssai); + INT8_TO_OCTET_STRING(plmn->nssai[k].sst, &nssai->sst); + if (plmn->nssai[k].sd) { asn1cCalloc(nssai->sd, sd); INT24_TO_OCTET_STRING(plmn->nssai[k].sd, sd); } + } + } + } + + return pdu; +} + +/** + * @brief XnAP Setup Response decoding + */ +bool decode_xn_setup_response(xnap_setup_resp_t *out, const XNAP_XnAP_PDU_t *pdu) +{ + /* Check presence of message type */ + _EQ_CHECK_INT(pdu->present, XNAP_XnAP_PDU_PR_successfulOutcome); + AssertError(pdu->choice.successfulOutcome != NULL, return false, "successfulOutcome is NULL"); + _EQ_CHECK_LONG(pdu->choice.successfulOutcome->procedureCode, XNAP_ProcedureCode_id_xnSetup); + _EQ_CHECK_INT(pdu->choice.successfulOutcome->value.present, XNAP_SuccessfulOutcome__value_PR_XnSetupResponse); + + /* XnSetupResponse container */ + XNAP_XnSetupResponse_t *in = &pdu->choice.successfulOutcome->value.choice.XnSetupResponse; + XNAP_XnSetupResponse_IEs_t *ie; + + /* Check presence of mandatory IEs */ + XNAP_LIB_FIND_IE(XNAP_XnSetupResponse_IEs_t, ie, &in->protocolIEs.list, XNAP_ProtocolIE_ID_id_GlobalNG_RAN_node_ID, true); + XNAP_LIB_FIND_IE(XNAP_XnSetupResponse_IEs_t, ie, &in->protocolIEs.list, XNAP_ProtocolIE_ID_id_TAISupport_list, true); + + /* Loop over all IEs */ + for (int i = 0; i < in->protocolIEs.list.count; i++) { + AssertError(in->protocolIEs.list.array[i] != NULL, return false, "protocolIEs.list.array[i] is NULL"); + ie = in->protocolIEs.list.array[i]; + + switch (ie->id) { + + case XNAP_ProtocolIE_ID_id_GlobalNG_RAN_node_ID: { + _EQ_CHECK_INT(ie->value.present, XNAP_XnSetupResponse_IEs__value_PR_GlobalNG_RANNode_ID); + XNAP_GlobalNG_RANNode_ID_t *global = &ie->value.choice.GlobalNG_RANNode_ID; + AssertError(global->present == XNAP_GlobalNG_RANNode_ID_PR_gNB, return false, "Only gNB supported"); + AssertError(global->choice.gNB != NULL, return false, "gNB is NULL"); + + PLMNID_TO_MCC_MNC(&global->choice.gNB->plmn_id, out->plmn.mcc, out->plmn.mnc, out->plmn.mnc_digit_length); + AssertError(global->choice.gNB->gnb_id.present == XNAP_GNB_ID_Choice_PR_gnb_ID, return false, "Unsupported gNB ID"); + MACRO_BIT_STRING_TO_GNB_ID(&global->choice.gNB->gnb_id.choice.gnb_ID, out->gNB_id); + } break; + + case XNAP_ProtocolIE_ID_id_TAISupport_list: { + _EQ_CHECK_INT(ie->value.present, XNAP_XnSetupResponse_IEs__value_PR_TAISupport_List); + out->num_tai = ie->value.choice.TAISupport_List.list.count; + out->tai_support = calloc_or_fail(out->num_tai, sizeof(*out->tai_support)); + + for (int m = 0; m < out->num_tai; m++) { + XNAP_TAISupport_Item_t *tai = ie->value.choice.TAISupport_List.list.array[m]; + OCTET_STRING_TO_INT24(&tai->tac, out->tai_support[m].tac); + + out->tai_support[m].num_plmn = tai->broadcastPLMNs.list.count; + out->tai_support[m].plmn_support = calloc_or_fail(out->tai_support[m].num_plmn, sizeof(*out->tai_support[m].plmn_support)); + + for (int j = 0; j < out->tai_support[m].num_plmn; j++) { + XNAP_BroadcastPLMNinTAISupport_Item_t *plmn_item = tai->broadcastPLMNs.list.array[j]; + xnap_plmn_support_t *plmn = &out->tai_support[m].plmn_support[j]; + + PLMNID_TO_MCC_MNC(&plmn_item->plmn_id, plmn->plmn.mcc, plmn->plmn.mnc, plmn->plmn.mnc_digit_length); + + plmn->num_nssai = plmn_item->tAISliceSupport_List.list.count; + plmn->nssai = calloc_or_fail(plmn->num_nssai, sizeof(*plmn->nssai)); + + for (int k = 0; k < plmn->num_nssai; k++) { + OCTET_STRING_TO_INT8(&plmn_item->tAISliceSupport_List.list.array[k]->sst, plmn->nssai[k].sst); + if (plmn_item->tAISliceSupport_List.list.array[k]->sd) { + OCTET_STRING_TO_INT24(plmn_item->tAISliceSupport_List.list.array[k]->sd, plmn->nssai[k].sd); + } + } + } + } + } break; + + default: + AssertError(0, return false, "Unknown XnAP IE id %ld\n", ie->id); + break; + } + } + return true; +} + +/** + * @brief XnAP Setup Response main equality check + */ +bool eq_xnap_setup_response(const xnap_setup_resp_t *a, const xnap_setup_resp_t *b) +{ + _EQ_CHECK_UINT32(a->gNB_id, b->gNB_id); + if (!eq_xnap_plmn(&a->plmn, &b->plmn)) return false; + + _EQ_CHECK_INT(a->num_tai, b->num_tai); + for (int i = 0; i < a->num_tai; i++) { + if (!eq_xnap_tai_support(&a->tai_support[i], &b->tai_support[i])) + return false; + } + + return true; +} + +/** + * @brief XnAP Setup Response memory management + */ +void free_xnap_setup_response(const xnap_setup_resp_t *msg) +{ + DevAssert(msg != NULL); + free_xnap_tai_support(msg->tai_support, msg->num_tai); +} + diff --git a/openair2/XNAP/lib/xnap_gNB_interface_management.h b/openair2/XNAP/lib/xnap_gNB_interface_management.h index a4e65d6e00..aab6f777e2 100644 --- a/openair2/XNAP/lib/xnap_gNB_interface_management.h +++ b/openair2/XNAP/lib/xnap_gNB_interface_management.h @@ -27,11 +27,15 @@ typedef struct XNAP_XnAP_PDU XNAP_XnAP_PDU_t; XNAP_XnAP_PDU_t *encode_xn_setup_request(const xnap_setup_req_t *req); +XNAP_XnAP_PDU_t *encode_xn_setup_response(const xnap_setup_resp_t *resp); bool decode_xn_setup_request(xnap_setup_req_t *req, const XNAP_XnAP_PDU_t *pdu); +bool decode_xn_setup_response(xnap_setup_resp_t *out, const XNAP_XnAP_PDU_t *pdu); bool eq_xnap_setup_request(const xnap_setup_req_t *a, const xnap_setup_req_t *b); +bool eq_xnap_setup_response(const xnap_setup_resp_t *a, const xnap_setup_resp_t *b); void free_xnap_setup_request(const xnap_setup_req_t *msg); +void free_xnap_setup_response(const xnap_setup_resp_t *msg); #endif /* XNAP_GNB_INTERFACE_MANAGEMENT_H_ */ diff --git a/openair2/XNAP/lib/xnap_lib_includes.h b/openair2/XNAP/lib/xnap_lib_includes.h index a67919413a..c45d9cf605 100644 --- a/openair2/XNAP/lib/xnap_lib_includes.h +++ b/openair2/XNAP/lib/xnap_lib_includes.h @@ -29,6 +29,7 @@ #include "XNAP_XnAP-PDU.h" #include "XNAP_ProtocolIE-Field.h" #include "XNAP_InitiatingMessage.h" +#include "XNAP_SuccessfulOutcome.h" #include "XNAP_GlobalgNB-ID.h" #include "XNAP_GlobalNG-RANNode-ID.h" @@ -37,6 +38,5 @@ #include "XNAP_S-NSSAI.h" #include "XNAP_BroadcastPLMNinTAISupport-Item.h" - #endif // XNAP_LIB_INCLUDES_H diff --git a/openair2/XNAP/tests/xnap_lib_test.c b/openair2/XNAP/tests/xnap_lib_test.c index c17f6b4967..610dc7d744 100644 --- a/openair2/XNAP/tests/xnap_lib_test.c +++ b/openair2/XNAP/tests/xnap_lib_test.c @@ -165,11 +165,96 @@ static void test_xn_setup_request(void) printf("%s() successful \n", __func__); } +/** + * 2. Xn Setup Response + */ +static void test_xn_setup_response(void) +{ + /* ---------- Common PLMNs ---------- */ + plmn_id_t plmn0 = { .mcc = 208, .mnc = 95, .mnc_digit_length = 2 }; + plmn_id_t plmn1 = { .mcc = 208, .mnc = 93, .mnc_digit_length = 2 }; + + /* ---------- TAI support (2) ---------- */ + xnap_tai_support_t *tai_support = calloc_or_fail(2, sizeof(*tai_support)); + + /* ================= TAI 0 ================= */ + tai_support[0].tac = 1; + tai_support[0].num_plmn = 2; + tai_support[0].plmn_support = calloc_or_fail(2, sizeof(*tai_support[0].plmn_support)); + + /* ---- TAI 0 / PLMN 0 ---- */ + tai_support[0].plmn_support[0].plmn = plmn0; + tai_support[0].plmn_support[0].num_nssai = 3; + tai_support[0].plmn_support[0].nssai = calloc_or_fail(3, sizeof(nssai_t)); + + tai_support[0].plmn_support[0].nssai[0] = (nssai_t){ .sst = 1, .sd = 0x010203 }; + tai_support[0].plmn_support[0].nssai[1] = (nssai_t){ .sst = 1, .sd = 0x010204 }; + tai_support[0].plmn_support[0].nssai[2] = (nssai_t){ .sst = 1, .sd = 0x010205 }; + + /* ---- TAI 0 / PLMN 1 ---- */ + tai_support[0].plmn_support[1].plmn = plmn1; + tai_support[0].plmn_support[1].num_nssai = 3; + tai_support[0].plmn_support[1].nssai = calloc_or_fail(3, sizeof(nssai_t)); + + tai_support[0].plmn_support[1].nssai[0] = (nssai_t){ .sst = 2, .sd = 0x020203 }; + tai_support[0].plmn_support[1].nssai[1] = (nssai_t){ .sst = 2, .sd = 0x020204 }; + tai_support[0].plmn_support[1].nssai[2] = (nssai_t){ .sst = 2, .sd = 0x020205 }; + + /* ================= TAI 1 ================= */ + tai_support[1].tac = 2; + tai_support[1].num_plmn = 2; + tai_support[1].plmn_support = calloc_or_fail(2, sizeof(*tai_support[1].plmn_support)); + + /* ---- TAI 1 / PLMN 0 ---- */ + tai_support[1].plmn_support[0].plmn = plmn0; + tai_support[1].plmn_support[0].num_nssai = 3; + tai_support[1].plmn_support[0].nssai = calloc_or_fail(3, sizeof(nssai_t)); + + tai_support[1].plmn_support[0].nssai[0] = (nssai_t){ .sst = 3, .sd = 0x030203 }; + tai_support[1].plmn_support[0].nssai[1] = (nssai_t){ .sst = 3, .sd = 0x030204 }; + tai_support[1].plmn_support[0].nssai[2] = (nssai_t){ .sst = 3, .sd = 0x030205 }; + + /* ---- TAI 1 / PLMN 1 ---- */ + tai_support[1].plmn_support[1].plmn = plmn1; + tai_support[1].plmn_support[1].num_nssai = 3; + tai_support[1].plmn_support[1].nssai = calloc_or_fail(3, sizeof(nssai_t)); + + tai_support[1].plmn_support[1].nssai[0] = (nssai_t){ .sst = 4, .sd = 0x040203 }; + tai_support[1].plmn_support[1].nssai[1] = (nssai_t){ .sst = 4, .sd = 0x040204 }; + tai_support[1].plmn_support[1].nssai[2] = (nssai_t){ .sst = 4, .sd = 0x040205 }; + + /* ---------- create message ---------- */ + xnap_setup_resp_t orig = { + .gNB_id = 0xABCDE, + .plmn = plmn0, + .num_tai = 2, + .tai_support = tai_support, + }; + /* ---------- encode ---------- */ + XNAP_XnAP_PDU_t *xnenc = encode_xn_setup_response(&orig); + XNAP_XnAP_PDU_t *xndec = xnap_encode_decode(xnenc); + xnap_msg_free(xnenc); + + /* ---------- decode ---------- */ + xnap_setup_resp_t decoded = {0}; + bool ret = decode_xn_setup_response(&decoded, xndec); + AssertFatal(ret, "decode_xn_setup_response failed"); + xnap_msg_free(xndec); + + /* ---------- equality ---------- */ + ret = eq_xnap_setup_response(&orig, &decoded); + AssertFatal(ret, "Xn Setup Response mismatch\n"); + free_xnap_setup_response(&decoded); + free_xnap_setup_response(&orig); + printf("%s() successful \n", __func__); +} + int main() { printf("Starting XnAP Library Unit Tests...\n"); /* Xn Interface Testing */ test_xn_setup_request(); + test_xn_setup_response(); printf("All XnAP tests passed!\n"); return 0;