From bf3b8b19efa0ca8203d0666d3214eb8f4d015748 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 20 Dec 2024 09:38:28 +0100 Subject: [PATCH 01/48] Fix missing line break in e1ap.c --- openair2/E1AP/e1ap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/E1AP/e1ap.c b/openair2/E1AP/e1ap.c index 95046cb652..e9aa7f54df 100644 --- a/openair2/E1AP/e1ap.c +++ b/openair2/E1AP/e1ap.c @@ -1129,7 +1129,7 @@ static void e1_task_handle_sctp_association_resp(E1_t type, if (getCxtE1(instance)->gtpInstF1U < 0) getCxtE1(instance)->gtpInstF1U = cuup_task_create_gtpu_instance_to_du(&IPaddr); if (getCxtE1(instance)->gtpInstF1U < 0) - LOG_E(E1AP, "Failed to create CUUP F1-U UDP listener"); + LOG_E(E1AP, "Failed to create CUUP F1-U UDP listener\n"); extern instance_t CUuniqInstance; CUuniqInstance = getCxtE1(instance)->gtpInstF1U; cuup_init_n3(instance); @@ -1152,7 +1152,7 @@ void cuup_init_n3(instance_t instance) legacyInstanceMapping = getCxtE1(instance)->gtpInstN3 = gtpv1Init(tmp); } if (getCxtE1(instance)->gtpInstN3 < 0) - LOG_E(E1AP, "Failed to create CUUP N3 UDP listener"); + LOG_E(E1AP, "Failed to create CUUP N3 UDP listener\n"); extern instance_t *N3GTPUInst; N3GTPUInst = &getCxtE1(instance)->gtpInstN3; } From a0da09635154a74d59f298705c2f00bf924300b9 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 4 Dec 2024 11:55:33 +0100 Subject: [PATCH 02/48] Fix equality check types in Bearer Context Setup Request/Response --- openair2/E1AP/lib/e1ap_bearer_context_management.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/openair2/E1AP/lib/e1ap_bearer_context_management.c b/openair2/E1AP/lib/e1ap_bearer_context_management.c index 5e359512ed..a67d4b5b08 100644 --- a/openair2/E1AP/lib/e1ap_bearer_context_management.c +++ b/openair2/E1AP/lib/e1ap_bearer_context_management.c @@ -263,7 +263,7 @@ static bool e1_decode_pdu_session_to_setup_item(pdu_session_to_setup_t *out, E1A E1AP_DRB_To_Setup_List_NG_RAN_t *drb2SetupList = &item->dRB_To_Setup_List_NG_RAN; _E1_EQ_CHECK_GENERIC(drb2SetupList->list.count > 0, "%d", drb2SetupList->list.count, 0); out->numDRB2Setup = drb2SetupList->list.count; - _E1_EQ_CHECK_INT(out->numDRB2Setup, 1); // can only handle one DRB per PDU session + _E1_EQ_CHECK_LONG(out->numDRB2Setup, 1UL); // can only handle one DRB per PDU session for (int j = 0; j < drb2SetupList->list.count; j++) { DRB_nGRAN_to_setup_t *drb = out->DRBnGRanList + j; E1AP_DRB_To_Setup_Item_NG_RAN_t *drb2Setup = drb2SetupList->list.array[j]; @@ -492,8 +492,7 @@ bool decode_E1_bearer_context_setup_request(const E1AP_E1AP_PDU_t *pdu, e1ap_bea // Check message type _E1_EQ_CHECK_INT(pdu->present,E1AP_E1AP_PDU_PR_initiatingMessage); - _E1_EQ_CHECK_INT(pdu->choice.initiatingMessage->procedureCode, E1AP_ProcedureCode_id_bearerContextSetup); - _E1_EQ_CHECK_INT(pdu->choice.initiatingMessage->criticality, E1AP_Criticality_reject); + _E1_EQ_CHECK_LONG(pdu->choice.initiatingMessage->procedureCode, E1AP_ProcedureCode_id_bearerContextSetup); _E1_EQ_CHECK_INT(pdu->choice.initiatingMessage->value.present, E1AP_InitiatingMessage__value_PR_BearerContextSetupRequest); const E1AP_BearerContextSetupRequest_t *in = &pdu->choice.initiatingMessage->value.choice.BearerContextSetupRequest; @@ -828,8 +827,7 @@ bool decode_E1_bearer_context_setup_response(const E1AP_E1AP_PDU_t *pdu, e1ap_be { // Check message type _E1_EQ_CHECK_INT(pdu->present, E1AP_E1AP_PDU_PR_successfulOutcome); - _E1_EQ_CHECK_INT(pdu->choice.successfulOutcome->procedureCode, E1AP_ProcedureCode_id_bearerContextSetup); - _E1_EQ_CHECK_INT(pdu->choice.successfulOutcome->criticality, E1AP_Criticality_reject); + _E1_EQ_CHECK_LONG(pdu->choice.successfulOutcome->procedureCode, E1AP_ProcedureCode_id_bearerContextSetup); _E1_EQ_CHECK_INT(pdu->choice.successfulOutcome->value.present, E1AP_SuccessfulOutcome__value_PR_BearerContextSetupResponse); const E1AP_BearerContextSetupResponse_t *in = &pdu->choice.successfulOutcome->value.choice.BearerContextSetupResponse; @@ -862,7 +860,7 @@ bool decode_E1_bearer_context_setup_response(const E1AP_E1AP_PDU_t *pdu, e1ap_be ie->value.choice.System_BearerContextSetupResponse.choice.nG_RAN_BearerContextSetupResponse; _E1_EQ_CHECK_INT(msgNGRAN_list->list.count, 1); E1AP_NG_RAN_BearerContextSetupResponse_t *msgNGRAN = msgNGRAN_list->list.array[0]; - _E1_EQ_CHECK_INT(msgNGRAN->id, E1AP_ProtocolIE_ID_id_PDU_Session_Resource_Setup_List); + _E1_EQ_CHECK_LONG(msgNGRAN->id, E1AP_ProtocolIE_ID_id_PDU_Session_Resource_Setup_List); _E1_EQ_CHECK_INT(msgNGRAN->value.present, E1AP_NG_RAN_BearerContextSetupResponse__value_PR_PDU_Session_Resource_Setup_List); E1AP_PDU_Session_Resource_Setup_List_t *pduSetupList = &msgNGRAN->value.choice.PDU_Session_Resource_Setup_List; From e9ff62884422725fbe92d2e7f84a71f43bb4bcb0 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 19 Nov 2024 18:55:29 +0100 Subject: [PATCH 03/48] Add macros to deep copy and compare optional E1 IEs --- openair2/E1AP/lib/e1ap_lib_common.h | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/openair2/E1AP/lib/e1ap_lib_common.h b/openair2/E1AP/lib/e1ap_lib_common.h index f3c6b24da2..eceddb263f 100644 --- a/openair2/E1AP/lib/e1ap_lib_common.h +++ b/openair2/E1AP/lib/e1ap_lib_common.h @@ -66,4 +66,36 @@ } \ } while (0) +/* deep copy of optional E1AP IE */ +#define _E1_CP_OPTIONAL_IE(dest, src, field) \ + do { \ + if ((src)->field) { \ + (dest)->field = malloc_or_fail(sizeof(*(dest)->field)); \ + *(dest)->field = *(src)->field; \ + } \ + } while (0) + +#define _E1_EQ_CHECK_OPTIONAL_IE(a, b, field, EQ_MACRO) \ + do { \ + if (((a)->field && !(b)->field) || (!(a)->field && (b)->field)) { \ + return false; /* One of the two is not allocated */ \ + } \ + if ((a)->field && (b)->field) { \ + EQ_MACRO(*(a)->field, *(b)->field) \ + } \ + } while (0) + +#define _E1_EQ_CHECK_OPTIONAL_PTR(a, b, field) \ + do { \ + if (((a)->field && !(b)->field) || (!(a)->field && (b)->field)) { \ + PRINT_ERROR("%s:%d: optional IE '%s' not allocated: %p, %p\n", \ + __FILE__, \ + __LINE__, \ + #field, \ + (void *)(a)->field, \ + (void *)(b)->field); \ + return false; /* One of the two is not allocated */ \ + } \ + } while (0) + #endif /* E1AP_LIB_COMMON_H_ */ From fd20068f269e059441e545ba9055069404a7922c Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Fri, 31 Jan 2025 15:47:41 +0100 Subject: [PATCH 04/48] Deadlock avoidance in rfsimulator --- radio/rfsimulator/simulator.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index faa185a0cb..262cb87deb 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -973,7 +973,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest } } else { bool have_to_wait; - + int loops = 0; do { have_to_wait=false; @@ -995,6 +995,12 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest t->nextRxTstamp + nsamps); flushInput(t, 3, nsamps); } + if (loops++ > 10 && t->role == SIMU_ROLE_SERVER) { + // Just start producing samples. The clients will catch up. + have_to_wait = false; + LOG_W(HW, + "No longer waiting for clients to catch up, starting to produce samples\n"); + } } while (have_to_wait); } From d791d03e0577cec48b0b868a4621ceaf649f42e4 Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 16 Sep 2024 15:13:45 +0200 Subject: [PATCH 05/48] fix to have correct maxMIMO_layers in dlsim computing tbslbrm --- openair1/SIMULATION/NR_PHY/dlsim.c | 1 + openair2/RRC/NR/nr_rrc_config.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index fc09f23bd6..72f4705520 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -658,6 +658,7 @@ int main(int argc, char **argv) .minRXTXTIME = 6, .do_CSIRS = 0, .do_SRS = 0, + .maxMIMO_layers = g_nrOfLayers, .force_256qam_off = false, .timer_config.sr_ProhibitTimer = 0, .timer_config.sr_TransMax = 64, diff --git a/openair2/RRC/NR/nr_rrc_config.c b/openair2/RRC/NR/nr_rrc_config.c index 18487e9657..86ad6e3469 100644 --- a/openair2/RRC/NR/nr_rrc_config.c +++ b/openair2/RRC/NR/nr_rrc_config.c @@ -566,9 +566,8 @@ static void config_csiim(int do_csirs, void set_dl_maxmimolayers(NR_PDSCH_ServingCellConfig_t *pdsch_servingcellconfig, const NR_ServingCellConfigCommon_t *scc, const NR_UE_NR_Capability_t *uecap, - int maxMIMO_layers) + int maxMIMO_layers) { - if(!pdsch_servingcellconfig->ext1) pdsch_servingcellconfig->ext1=calloc(1,sizeof(*pdsch_servingcellconfig->ext1)); if(!pdsch_servingcellconfig->ext1->maxMIMO_Layers) @@ -871,6 +870,8 @@ void prepare_sim_uecap(NR_UE_NR_Capability_t *cap, fs_cc->supportedBandwidthDL.present = NR_SupportedBandwidth_PR_fr1; fs_cc->supportedBandwidthDL.choice.fr1 = bw; } + fs_cc->maxNumberMIMO_LayersPDSCH = calloc(1, sizeof(*fs_cc->maxNumberMIMO_LayersPDSCH)); + *fs_cc->maxNumberMIMO_LayersPDSCH = NR_MIMO_LayersDL_fourLayers; fs_cc->supportedModulationOrderDL = calloc(1, sizeof(*fs_cc->supportedModulationOrderDL)); *fs_cc->supportedModulationOrderDL = NR_ModulationOrder_qam256; asn1cSeqAdd(&fs->featureSetsDownlinkPerCC->list, fs_cc); From fc52fb8d24b0c2f22aedab84eb40fa2a80e88144 Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 16 Sep 2024 15:36:37 +0200 Subject: [PATCH 06/48] compute_tbs_common formatting --- .../NR_MAC_COMMON/nr_compute_tbs_common.c | 133 ++++++++---------- 1 file changed, 61 insertions(+), 72 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c index 1c35fcea63..3ce0867383 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_compute_tbs_common.c @@ -63,95 +63,84 @@ uint32_t nr_compute_tbs(uint16_t Qm, const uint32_t nb_re = min(156, nbp_re) * nb_rb; // Intermediate number of information bits // Rx1024 is tabulated as 10 times the actual code rate - const uint32_t R_5 = R/5; // R can be fractional so we can't divide by 10 + const uint32_t R_5 = R / 5; // R can be fractional so we can't divide by 10 // So we ned to right shift by 11 (10 for x1024 and 1 additional as above) - const uint32_t Ninfo = ((nb_re * R_5 * Qm * Nl)>>11)>>tb_scaling; - - uint32_t nr_tbs=0; + const uint32_t Ninfo = ((nb_re * R_5 * Qm * Nl) >> 11) >> tb_scaling; + uint32_t nr_tbs = 0; uint32_t Np_info, C, n; - if (Ninfo <= NR_MAX_PDSCH_TBS) { n = max(3, floor(log2(Ninfo)) - 6); - Np_info = max(24, (Ninfo>>n)<= Np_info){ - nr_tbs = Tbstable_nr[i]; - break; - } - } - } else { - n = log2(Ninfo-24)-5; - Np_info = max(3840, (ROUNDIDIV((Ninfo-24),(1< 8424){ - C = CEILIDIV((Np_info+24),8424); - nr_tbs = (C<<3)*CEILIDIV((Np_info+24),(C<<3)) - 24; - } else { - nr_tbs = ((CEILIDIV((Np_info+24),8))<<3) - 24; - } - } - } - - LOG_D(NR_MAC, "Ninfo %u nbp_re %d nb_re %d Qm %d, R %d, tbs %d bits\n", Ninfo, nbp_re, nb_re, Qm, R, nr_tbs); - - return nr_tbs; -} - -//tbslbrm calculation according to 5.4.2.1 of 38.212 -uint32_t nr_compute_tbslbrm(uint16_t table, - uint16_t nb_rb, - uint8_t Nl) { - - uint16_t R, nb_re; - uint16_t nb_rb_lbrm=0; - uint8_t Qm; - int i; - uint32_t nr_tbs=0; - uint32_t Ninfo, Np_info, C; - uint8_t n; - - for (i=0; i<7; i++) { - if (NPRB_LBRM[i] >= nb_rb){ - nb_rb_lbrm = NPRB_LBRM[i]; - break; - } - } - - Qm = ((table == 1)? 8 : 6); - R = 948; - nb_re = 156 * nb_rb_lbrm; - - // Intermediate number of information bits - Ninfo = (nb_re * R * Qm * Nl)>>10; - - if (Ninfo <= NR_MAX_PDSCH_TBS) { - n = max(3, floor(log2(Ninfo)) - 6); - Np_info = max(24, (Ninfo>>n)<> n) << n); + for (int i = 0; i < INDEX_MAX_TBS_TABLE; i++) { if (Tbstable_nr[i] >= Np_info){ nr_tbs = Tbstable_nr[i]; break; } } } else { - n = log2(Ninfo-24)-5; - Np_info = max(3840, (ROUNDIDIV((Ninfo-24),(1< 8424){ + C = CEILIDIV((Np_info + 24), 8424); + nr_tbs = (C << 3) * CEILIDIV((Np_info + 24), (C << 3)) - 24; + } else { + nr_tbs = ((CEILIDIV((Np_info + 24), 8)) << 3) - 24; + } + } + } + + LOG_D(NR_MAC, "Ninfo %u nbp_re %d nb_re %d Qm %d, R %d, tbs %d bits\n", Ninfo, nbp_re, nb_re, Qm, R, nr_tbs); + return nr_tbs; +} + +//tbslbrm calculation according to 5.4.2.1 of 38.212 +uint32_t nr_compute_tbslbrm(uint16_t table, uint16_t nb_rb, uint8_t Nl) +{ + uint16_t nb_rb_lbrm = 0; + for (int i = 0; i < 7; i++) { + if (NPRB_LBRM[i] >= nb_rb){ + nb_rb_lbrm = NPRB_LBRM[i]; + break; + } + } + + int Qm = (table == 1) ? 8 : 6; + uint32_t R = 948; + uint32_t nb_re = 156 * nb_rb_lbrm; + + // Intermediate number of information bits + uint32_t Ninfo = (nb_re * R * Qm * Nl) >> 10; + uint32_t nr_tbs = 0; + uint32_t Np_info, n; + if (Ninfo <= NR_MAX_PDSCH_TBS) { + n = max(3, floor(log2(Ninfo)) - 6); + Np_info = max(24, (Ninfo >> n) << n); + for (int i = 0; i < INDEX_MAX_TBS_TABLE; i++) { + if (Tbstable_nr[i] >= Np_info){ + nr_tbs = Tbstable_nr[i]; + break; + } + } + } else { + n = log2(Ninfo - 24) - 5; + Np_info = max(3840, (ROUNDIDIV((Ninfo - 24), (1 << n))) << n); + int C; if (R <= 256) { - C = CEILIDIV((Np_info+24),3816); - nr_tbs = (C<<3)*CEILIDIV((Np_info+24),(C<<3)) - 24; + C = CEILIDIV((Np_info + 24), 3816); + nr_tbs = (C << 3) * CEILIDIV((Np_info + 24), (C << 3)) - 24; } else { if (Np_info > 8424){ - C = CEILIDIV((Np_info+24),8424); - nr_tbs = (C<<3)*CEILIDIV((Np_info+24),(C<<3)) - 24; + C = CEILIDIV((Np_info + 24), 8424); + nr_tbs = (C << 3) * CEILIDIV((Np_info + 24), (C << 3)) - 24; } else - nr_tbs = ((CEILIDIV((Np_info+24),8))<<3) - 24; + nr_tbs = ((CEILIDIV((Np_info + 24), 8)) << 3) - 24; } } return nr_tbs; From 3bfb95298958bc4834647d01fbdff31d273f97d5 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 17 Sep 2024 17:41:38 +0200 Subject: [PATCH 07/48] rate matching formatting --- .../nrLDPC_coding_segment/nr_rate_matching.c | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nr_rate_matching.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nr_rate_matching.c index a25b6d4b57..46919e1983 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nr_rate_matching.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nr_rate_matching.c @@ -380,24 +380,22 @@ int nr_rate_matching_ldpc(uint32_t Tbslbrm, uint8_t rvidx, uint32_t E) { - uint32_t Ncb, ind, k = 0, Nref, N; - if (C == 0) { - LOG_E(PHY, "nr_rate_matching: invalid parameters (C %d\n", C); + LOG_E(PHY, "nr_rate_matching: invalid parameter C %d\n", C); return -1; } - // Bit selection - N = (BG == 1) ? (66 * Z) : (50 * Z); - + //Bit selection + uint32_t N = (BG == 1) ? (66 * Z) : (50 * Z); + uint32_t Ncb; if (Tbslbrm == 0) Ncb = N; else { - Nref = 3 * Tbslbrm / (2 * C); // R_LBRM = 2/3 + uint32_t Nref = 3 * Tbslbrm / (2 * C); //R_LBRM = 2/3 Ncb = min(N, Nref); } - ind = (index_k0[BG - 1][rvidx] * Ncb / N) * Z; + uint32_t ind = (index_k0[BG - 1][rvidx] * Ncb / N) * Z; #ifdef RM_DEBUG printf("nr_rate_matching_ldpc: E %u, F %u, Foffset %u, k0 %u, Ncb %u, rvidx %d, Tbslbrm %u\n", @@ -430,6 +428,7 @@ int nr_rate_matching_ldpc(uint32_t Tbslbrm, if (ind >= Foffset && ind < (F + Foffset)) ind = F + Foffset; + uint32_t k = 0; if (ind < Foffset) { // case where we have some bits before the filler and the rest after memcpy((void *)e, (void *)(d + ind), Foffset - ind); @@ -476,28 +475,22 @@ int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm, uint32_t F, uint32_t Foffset) { - uint32_t Ncb, ind, k, Nref, N; - -#ifdef RM_DEBUG - int nulled = 0; -#endif - if (C == 0) { - LOG_E(PHY, "nr_rate_matching: invalid parameters (C %d\n", C); + LOG_E(PHY, "nr_rate_matching: invalid parameter C %d\n", C); return -1; } - // Bit selection - N = (BG == 1) ? (66 * Z) : (50 * Z); - + //Bit selection + uint32_t N = (BG == 1) ? (66 * Z) : (50 * Z); + uint32_t Ncb; if (Tbslbrm == 0) Ncb = N; else { - Nref = (3 * Tbslbrm / (2 * C)); // R_LBRM = 2/3 + uint32_t Nref = (3 * Tbslbrm / (2 * C)); //R_LBRM = 2/3 Ncb = min(N, Nref); } - ind = (index_k0[BG - 1][rvidx] * Ncb / N) * Z; + uint32_t ind = (index_k0[BG - 1][rvidx] * Ncb / N) * Z; if (Foffset > E) { LOG_E(PHY, "nr_rate_matching: invalid parameters (Foffset %d > E %d)\n", Foffset, E); return -1; @@ -521,8 +514,7 @@ int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm, if (clear == 1) memset(d, 0, Ncb * sizeof(int16_t)); - k = 0; - + uint32_t k = 0; if (ind < Foffset) for (; (ind < Foffset) && (k < E); ind++) { #ifdef RM_DEBUG @@ -566,6 +558,5 @@ int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm, #endif } } - return 0; } From 65cbcec57a4545ea6d74a822eb6612e90a11eb39 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 20 Sep 2024 12:22:32 +0200 Subject: [PATCH 08/48] fixes to allow FR2 dlsim test --- cmake_targets/autotests/test_case_list.xml | 8 ++-- common/utils/nr/nr_common.c | 4 +- openair1/SIMULATION/NR_PHY/dlsim.c | 5 +- openair2/GNB_APP/gnb_config.c | 55 +++++++++++++++------- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/cmake_targets/autotests/test_case_list.xml b/cmake_targets/autotests/test_case_list.xml index 32876634c0..7f712a434b 100755 --- a/cmake_targets/autotests/test_case_list.xml +++ b/cmake_targets/autotests/test_case_list.xml @@ -168,7 +168,8 @@ (Test4: HARQ test 25% TP 4 rounds), (Test5: HARQ test 33% TP 3 rounds), (Test6: HARQ test 50% TP 2 rounds), - (Test7: 25 PRBs, 15 kHz SCS) + (Test7: 25 PRBs, 15 kHz SCS), + (Test8: 32 PRBs, 120 kHz SCS) nr_dlsim -n100 -R106 -b106 -s5 -n100 -R217 -b217 -s5 @@ -176,8 +177,9 @@ -n100 -s1 -S2 -t25 -n100 -s1 -S2 -t33 -n100 -s5 -S7 -t50 - -n100 -m0 -e0 -R25 -b25 -i 2 1 0 - test1 test2 test3 test4 test5 test6 test7 + -n100 -m0 -e0 -R25 -b25 -i 2 1 0 + -n100 -s5 -m3 -R32 -b32 + test1 test2 test3 test4 test5 test6 test7 test8 PDSCH test OK segmentation fault|assertion|exiting|fatal 3 diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index 9b029657ad..5862c91990 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -423,9 +423,7 @@ int get_supported_bw_mhz(frequency_range_t frequency_range, int bw_index) { if (frequency_range == FR1) { int bandwidth_index_to_mhz[] = {5, 10, 15, 20, 25, 30, 40, 50, 60, 80, 90, 100}; - AssertFatal(bw_index >= 0 && bw_index <= sizeofArray(bandwidth_index_to_mhz), - "Bandwidth index %d is invalid\n", - bw_index); + AssertFatal(bw_index >= 0 && bw_index <= sizeofArray(bandwidth_index_to_mhz), "Bandwidth index %d is invalid\n", bw_index); return bandwidth_index_to_mhz[bw_index]; } else { int bandwidth_index_to_mhz[] = {50, 100, 200, 400}; diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 72f4705520..e9af64ed3c 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -147,10 +147,9 @@ extern void fix_scd(NR_ServingCellConfig_t *scd);// forward declaration /* specific dlsim DL preprocessor: uses rbStart/rbSize/mcs/nrOfLayers from command line of dlsim */ int g_mcsIndex = -1, g_mcsTableIdx = 0, g_rbStart = -1, g_rbSize = -1, g_nrOfLayers = 1, g_pmi = 0; -void nr_dlsim_preprocessor(module_id_t module_id, - frame_t frame, - sub_frame_t slot) { +void nr_dlsim_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t slot) +{ NR_UE_info_t *UE_info = RC.nrmac[module_id]->UE_info.list[0]; AssertFatal(RC.nrmac[module_id]->UE_info.list[1]==NULL, "can have only a single UE\n"); NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl; diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index a57d2e0287..730f08dd7d 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -274,15 +274,29 @@ void fill_scc_sim(NR_ServingCellConfigCommon_t *scc, uint64_t *ssb_bitmap, int N scc->dmrs_TypeA_Position = NR_ServingCellConfigCommon__dmrs_TypeA_Position_pos2; *scc->ssbSubcarrierSpacing = mu_dl; - struct NR_FrequencyInfoDL *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL; - if (mu_dl == 0) { - *frequencyInfoDL->absoluteFrequencySSB = 520432; - *frequencyInfoDL->frequencyBandList.list.array[0] = 38; - frequencyInfoDL->absoluteFrequencyPointA = 520000; - } else { - *frequencyInfoDL->absoluteFrequencySSB = 641032; - *frequencyInfoDL->frequencyBandList.list.array[0] = 78; - frequencyInfoDL->absoluteFrequencyPointA = 640000; + NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL; + NR_TDD_UL_DL_ConfigCommon_t *tdd_UL_DL_Config = scc->tdd_UL_DL_ConfigurationCommon; + switch (mu_dl) { + case 0 : + *frequencyInfoDL->absoluteFrequencySSB = 520432; + *frequencyInfoDL->frequencyBandList.list.array[0] = 38; + frequencyInfoDL->absoluteFrequencyPointA = 520000; + tdd_UL_DL_Config->pattern1.dl_UL_TransmissionPeriodicity = NR_TDD_UL_DL_Pattern__dl_UL_TransmissionPeriodicity_ms10; + break; + case 1 : + *frequencyInfoDL->absoluteFrequencySSB = 641032; + *frequencyInfoDL->frequencyBandList.list.array[0] = 78; + frequencyInfoDL->absoluteFrequencyPointA = 640000; + tdd_UL_DL_Config->pattern1.dl_UL_TransmissionPeriodicity = NR_TDD_UL_DL_Pattern__dl_UL_TransmissionPeriodicity_ms5; + break; + case 3 : + *frequencyInfoDL->absoluteFrequencySSB = 2071387; + *frequencyInfoDL->frequencyBandList.list.array[0] = 257; + frequencyInfoDL->absoluteFrequencyPointA = 2071003; + tdd_UL_DL_Config->pattern1.dl_UL_TransmissionPeriodicity = NR_TDD_UL_DL_Pattern__dl_UL_TransmissionPeriodicity_ms1p25; + break; + default : + AssertFatal(false, "Numerolgy %d not supported\n", mu_dl); } *frequencyInfoDL->scs_SpecificCarrierList.list.array[0] = configure_scs_carrier(mu_dl, N_RB_DL); @@ -306,7 +320,19 @@ void fill_scc_sim(NR_ServingCellConfigCommon_t *scc, uint64_t *ssb_bitmap, int N timedomainresourceallocation1); struct NR_FrequencyInfoUL *frequencyInfoUL = scc->uplinkConfigCommon->frequencyInfoUL; - *frequencyInfoUL->frequencyBandList->list.array[0] = mu_ul ? 78 : 38; + switch (mu_ul) { + case 0 : + *frequencyInfoUL->frequencyBandList->list.array[0] = 38; + break; + case 1 : + *frequencyInfoUL->frequencyBandList->list.array[0] = 78; + break; + case 3 : + *frequencyInfoUL->frequencyBandList->list.array[0] = 257; + break; + default : + AssertFatal(false, "Numerolgy %d not supported\n", mu_ul); + } *frequencyInfoUL->absoluteFrequencyPointA = -1; *frequencyInfoUL->scs_SpecificCarrierList.list.array[0] = configure_scs_carrier(mu_ul, N_RB_UL); *frequencyInfoUL->p_Max = 20; @@ -348,18 +374,15 @@ void fill_scc_sim(NR_ServingCellConfigCommon_t *scc, uint64_t *ssb_bitmap, int N scc->ssb_PositionsInBurst->present = NR_ServingCellConfigCommon__ssb_PositionsInBurst_PR_mediumBitmap; *ssb_bitmap = 0xff; - struct NR_TDD_UL_DL_ConfigCommon *tdd_UL_DL_ConfigurationCommon = scc->tdd_UL_DL_ConfigurationCommon; - tdd_UL_DL_ConfigurationCommon->referenceSubcarrierSpacing = mu_dl; + tdd_UL_DL_Config->referenceSubcarrierSpacing = mu_dl; - NR_TDD_UL_DL_Pattern_t *p1 = &tdd_UL_DL_ConfigurationCommon->pattern1; - p1->dl_UL_TransmissionPeriodicity = (mu_dl == 0) ? NR_TDD_UL_DL_Pattern__dl_UL_TransmissionPeriodicity_ms10 - : NR_TDD_UL_DL_Pattern__dl_UL_TransmissionPeriodicity_ms5; + NR_TDD_UL_DL_Pattern_t *p1 = &tdd_UL_DL_Config->pattern1; p1->nrofDownlinkSlots = 7; p1->nrofDownlinkSymbols = 6; p1->nrofUplinkSlots = 2; p1->nrofUplinkSymbols = 4; - struct NR_TDD_UL_DL_Pattern *p2 = tdd_UL_DL_ConfigurationCommon->pattern2; + struct NR_TDD_UL_DL_Pattern *p2 = tdd_UL_DL_Config->pattern2; if (p2) { p2->dl_UL_TransmissionPeriodicity = 321; p2->nrofDownlinkSlots = -1; From a772f91053689620531901985c5905cd5e8f2583 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 20 Sep 2024 12:26:33 +0200 Subject: [PATCH 09/48] add option to select slot in dlsim --- openair1/SIMULATION/NR_PHY/dlsim.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index e9af64ed3c..1f597a3c0b 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -356,7 +356,7 @@ int main(int argc, char **argv) FILE *scg_fd=NULL; - while ((c = getopt(argc, argv, "--:O:f:hA:p:f:g:i:n:s:S:t:v:x:y:z:o:M:N:F:GR:d:PI:L:a:b:e:m:w:T:U:q:X:Y:Z:")) != -1) { + while ((c = getopt(argc, argv, "--:O:f:hA:p:f:g:i:n:s:S:t:v:x:y:z:o:H:M:N:F:GR:d:PI:L:a:b:e:m:w:T:U:q:X:Y:Z:")) != -1) { /* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */ /* with this opstring getopt returns 1 for non-option arguments, refer to 'man 3 getopt' */ @@ -541,6 +541,7 @@ int main(int argc, char **argv) //target_error_rate=0.1; slot = 0; break; + case 'Z' : filename_csv = strdup(optarg); AssertFatal(filename_csv != NULL, "strdup() error: errno %d\n", errno); @@ -550,6 +551,10 @@ int main(int argc, char **argv) delay = atoi(optarg); break; + case 'H': + slot = atoi(optarg); + break; + default: case 'h': printf("%s -h(elp) -p(extended_prefix) -N cell_id -f output_filename -F input_filename -g channel_model -n n_frames -s snr0 -S snr1 -x transmission_mode -y TXant -z RXant -i Intefrence0 -j Interference1 -A interpolation_file -C(alibration offset dB) -N CellId\n", @@ -583,6 +588,7 @@ int main(int argc, char **argv) printf("-f raw file containing RRC configuration (generated by gNB)\n"); printf("-g Channel model: [A] TDLA30, [B] TDLB100, [C] TDLC300, e.g. -g A\n"); printf("-h This message\n"); + printf("-H Slot number\n"); printf("-i Change channel estimation technique. Arguments list: Frequency domain {0:Linear interpolation, 1:PRB based averaging}, Time domain {0:Estimates of last DMRS symbol, 1:Average of DMRS symbols}\n"); printf("-m Numerology\n"); printf("-n Number of frames to simulate\n"); From 92fc1639e843ea953153c2e45c51b7279d6b207b Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 11 Oct 2024 17:11:23 +0200 Subject: [PATCH 10/48] add FR2 ulsim test case --- cmake_targets/autotests/test_case_list.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cmake_targets/autotests/test_case_list.xml b/cmake_targets/autotests/test_case_list.xml index 7f712a434b..80f18e98a7 100755 --- a/cmake_targets/autotests/test_case_list.xml +++ b/cmake_targets/autotests/test_case_list.xml @@ -402,9 +402,10 @@ (Test9: PUSCH Type B, 3 DMRS, 2 PTRS, 7 Interpolated Symbols), (Test10: PUSCH Type B, 3 DMRS, 2 PTRS, 3 Interpolated Symbols), (Test11: 25 PRBs, 15 kHz SCS), - (Test12: MCS 0, low SNR performance) - (Test13: MCS 28, 106 PRBs, Time shift 8) - (Test14: SRS, SNR 40 dB) + (Test12: 32 PRBs, 120 kHz SCS), + (Test13: MCS 0, low SNR performance) + (Test14: MCS 28, 106 PRBs, Time shift 8) + (Test15: SRS, SNR 40 dB) nr_ulsim -n100 -m9 -r106 -s5 -n100 -m16 -s10 @@ -417,10 +418,11 @@ -n100 -s5 -T 2,2 -U 1,2,1,1 -n100 -s5 -a4 -b8 -T 1,2 -U 1,3,1,1 -n100 -u0 -m0 -R25 -r25 -i 1,0 + -n100 -s5 -r32 -R32 -u3 -n100 -m0 -S -0.6 -i 1,0 -n100 -m 28 -R106 -r106 -t90 -s24 -S24 -d 8 -n100 -s40 -E 1 - test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 test12 test13 test14 + test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 test12 test13 test14 test15 PUSCH test OK segmentation fault|assertion|exiting|fatal 3 From 1a3512960a474b065af69dfc201473b5750f5275 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 4 Feb 2025 09:39:11 +0100 Subject: [PATCH 11/48] check if the slot selected is valid for DL/UL in dlsim/ulsim --- openair1/SIMULATION/NR_PHY/dlsim.c | 9 +++++-- openair1/SIMULATION/NR_PHY/ulsim.c | 5 ++++ openair2/LAYER2/NR_MAC_gNB/config.c | 35 ++++++++++++++------------ openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 5 ++++ 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 1f597a3c0b..885684fe6e 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -537,8 +537,7 @@ int main(int argc, char **argv) break; case 'Y': - run_initial_sync=1; - //target_error_rate=0.1; + run_initial_sync = 1; slot = 0; break; @@ -552,6 +551,7 @@ int main(int argc, char **argv) break; case 'H': + AssertFatal(run_initial_sync == 0, "Cannot use Y and H options at the same time\n"); slot = atoi(optarg); break; @@ -653,6 +653,11 @@ int main(int argc, char **argv) fill_scc_sim(scc, &ssb_bitmap, N_RB_DL, N_RB_DL, mu, mu); fix_scc(scc, ssb_bitmap); + frame_structure_t frame_structure = {0}; + frame_type_t frame_type = TDD; + config_frame_structure(mu, scc, get_tdd_period_idx(scc->tdd_UL_DL_ConfigurationCommon), frame_type, &frame_structure); + AssertFatal(is_dl_slot(slot, &frame_structure), "The slot selected is not DL. Can't run DLSIM\n"); + // TODO do a UECAP for phy-sim nr_pdsch_AntennaPorts_t pdsch_AntennaPorts = {0}; pdsch_AntennaPorts.N1 = n_tx > 1 ? n_tx >> 1 : 1; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 301beb19f5..5688253b8d 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -626,6 +626,11 @@ int main(int argc, char *argv[]) fill_scc_sim(scc, &ssb_bitmap, N_RB_DL, N_RB_DL, mu, mu); fix_scc(scc,ssb_bitmap); + frame_structure_t frame_structure = {0}; + frame_type_t frame_type = TDD; + config_frame_structure(mu, scc, get_tdd_period_idx(scc->tdd_UL_DL_ConfigurationCommon), frame_type, &frame_structure); + AssertFatal(is_ul_slot(slot, &frame_structure), "The slot selected is not UL. Can't run ULSIM\n"); + // TODO do a UECAP for phy-sim const nr_mac_config_t conf = {.pdsch_AntennaPorts = {.N1 = 1, .N2 = 1, .XP = 1}, .pusch_AntennaPorts = n_rx, diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index fda1dc6c11..415cfc79e3 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -500,34 +500,28 @@ int get_ul_slot_offset(const frame_structure_t *fs, int idx, bool count_mixed) * * @param mu numerology * @param scc pointer to scc - * @param cfg pointer to NFAPI config request + * @param tdd_period TDD period + * @param frame_type type of frame structure (FDD or TDD) * @param fs pointer to the frame structure to update - * - * @return Number of periods in frame */ -static int config_frame_structure(int mu, - NR_ServingCellConfigCommon_t *scc, - nfapi_nr_config_request_scf_t *cfg, - frame_structure_t *fs) +void config_frame_structure(int mu, + NR_ServingCellConfigCommon_t *scc, + uint8_t tdd_period, + uint8_t frame_type, + frame_structure_t *fs) { fs->numb_slots_frame = nr_slots_per_frame[mu]; - if (cfg->cell_config.frame_duplex_type.value == TDD) { - cfg->tdd_table.tdd_period.tl.tag = NFAPI_NR_CONFIG_TDD_PERIOD_TAG; - cfg->num_tlv++; - cfg->tdd_table.tdd_period.value = get_tdd_period_idx(scc->tdd_UL_DL_ConfigurationCommon); - LOG_D(NR_MAC, "Setting TDD configuration period to %d\n", cfg->tdd_table.tdd_period.value); - fs->numb_period_frame = get_nb_periods_per_frame(cfg->tdd_table.tdd_period.value); + if (frame_type == TDD) { + fs->numb_period_frame = get_nb_periods_per_frame(tdd_period); fs->numb_slots_period = fs->numb_slots_frame / fs->numb_period_frame; fs->is_tdd = true; config_tdd_patterns(scc->tdd_UL_DL_ConfigurationCommon, fs); - set_tdd_config_nr(cfg, fs); } else { // FDD fs->is_tdd = false; fs->numb_period_frame = 1; fs->numb_slots_period = nr_slots_per_frame[mu]; } AssertFatal(fs->numb_period_frame > 0, "Frame configuration cannot be configured!\n"); - return fs->numb_period_frame; } static void config_common(gNB_MAC_INST *nrmac, @@ -819,7 +813,16 @@ static void config_common(gNB_MAC_INST *nrmac, // Frame structure configuration uint8_t mu = frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing; - config_frame_structure(mu, scc, cfg, &nrmac->frame_structure); + if (cfg->cell_config.frame_duplex_type.value == TDD) { + cfg->tdd_table.tdd_period.tl.tag = NFAPI_NR_CONFIG_TDD_PERIOD_TAG; + cfg->num_tlv++; + cfg->tdd_table.tdd_period.value = get_tdd_period_idx(scc->tdd_UL_DL_ConfigurationCommon); + LOG_D(NR_MAC, "Setting TDD configuration period to %d\n", cfg->tdd_table.tdd_period.value); + } + frame_structure_t *fs = &nrmac->frame_structure; + config_frame_structure(mu, scc, cfg->tdd_table.tdd_period.value, cfg->cell_config.frame_duplex_type.value, fs); + if (cfg->cell_config.frame_duplex_type.value == TDD) + set_tdd_config_nr(cfg, fs); int nb_tx = config->nb_bfw[0]; // number of tx antennas int nb_beams = config->nb_bfw[1]; // number of beams diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 4cd5563200..9be55c8dee 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -39,6 +39,11 @@ void set_cset_offset(uint16_t); void get_K1_K2(int N1, int N2, int *K1, int *K2, int layers); int get_NTN_Koffset(const NR_ServingCellConfigCommon_t *scc); +void config_frame_structure(int mu, + NR_ServingCellConfigCommon_t *scc, + uint8_t tdd_period, + uint8_t frame_type, + frame_structure_t *fs); int get_first_ul_slot(const frame_structure_t *fs, bool mixed); int get_ul_slots_per_period(const frame_structure_t *fs); int get_ul_slots_per_frame(const frame_structure_t *fs); From 43b981247eb5cee1c24109def6d0c162572e9682 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 4 Feb 2025 13:21:43 +0100 Subject: [PATCH 12/48] error-simd-intrinsic-name --- openair1/PHY/MODULATION/nr_modulation.c | 4 ++-- openair1/PHY/TOOLS/tools_defs.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openair1/PHY/MODULATION/nr_modulation.c b/openair1/PHY/MODULATION/nr_modulation.c index e8b215accc..6fd4ea76b3 100644 --- a/openair1/PHY/MODULATION/nr_modulation.c +++ b/openair1/PHY/MODULATION/nr_modulation.c @@ -517,7 +517,7 @@ void nr_layer_precoder_simd(const int n_layers, prec_weight.r = pmi_pdu->weights[nl][ant].precoder_weight_Re; prec_weight.i = pmi_pdu->weights[nl][ant].precoder_weight_Im; - const simde__m256i x = simde_mm256_loadu_epi32(&txdataF_res_mapped[nl][symbol][sc]); + const simde__m256i x = simde_mm256_loadu_si256(&txdataF_res_mapped[nl][symbol][sc]); // Rearrange precoding matrix weight to match complex multiplication and broadcast it to match SIMD size const simde__m256i w_c = simde_mm256_set1_epi32(c16toI32(c16conj(prec_weight))); // broadcast conjugate of w @@ -561,7 +561,7 @@ void nr_layer_precoder_simd(const int n_layers, prec_weight.r = pmi_pdu->weights[nl][ant].precoder_weight_Re; prec_weight.i = pmi_pdu->weights[nl][ant].precoder_weight_Im; - const simde__m128i x = simde_mm_loadu_epi32(&txdataF_res_mapped[nl][symbol][sc]); + const simde__m128i x = simde_mm_loadu_si128(&txdataF_res_mapped[nl][symbol][sc]); // Rearrange precoding matrix weight to match complex multiplication and broadcast it to match SIMD size const simde__m128i w_c = simde_mm_set1_epi32(c16toI32(c16conj(prec_weight))); // broadcast conjugate of w diff --git a/openair1/PHY/TOOLS/tools_defs.h b/openair1/PHY/TOOLS/tools_defs.h index b456fab82b..93b07a60de 100644 --- a/openair1/PHY/TOOLS/tools_defs.h +++ b/openair1/PHY/TOOLS/tools_defs.h @@ -401,8 +401,8 @@ static __attribute__((always_inline)) inline void mult_complex_vectors(const c16 int i; // do 8 multiplications at a time for (i = 0; i < size - 7; i += 8) { - const simde__m256i i1 = simde_mm256_loadu_epi32((simde__m256i *)(in1 + i)); - const simde__m256i i2 = simde_mm256_loadu_epi32((simde__m256i *)(in2 + i)); + const simde__m256i i1 = simde_mm256_loadu_si256((simde__m256i *)(in1 + i)); + const simde__m256i i2 = simde_mm256_loadu_si256((simde__m256i *)(in2 + i)); const simde__m256i i2swap = simde_mm256_shuffle_epi8(i2, complex_shuffle256); const simde__m256i i2conj = simde_mm256_sign_epi16(i2, conj256); const simde__m256i re = simde_mm256_madd_epi16(i1, i2conj); @@ -412,8 +412,8 @@ static __attribute__((always_inline)) inline void mult_complex_vectors(const c16 simde_mm256_blend_epi16(simde_mm256_srai_epi32(re, shift), simde_mm256_slli_epi32(im, 16 - shift), 0xAA)); } if (size - i > 4) { - const simde__m128i i1 = simde_mm_loadu_epi32((simde__m128i *)(in1 + i)); - const simde__m128i i2 = simde_mm_loadu_epi32((simde__m128i *)(in2 + i)); + const simde__m128i i1 = simde_mm_loadu_si128((simde__m128i *)(in1 + i)); + const simde__m128i i2 = simde_mm_loadu_si128((simde__m128i *)(in2 + i)); const simde__m128i i2swap = simde_mm_shuffle_epi8(i2, *(simde__m128i *)&complex_shuffle256); const simde__m128i i2conj = simde_mm_sign_epi16(i2, *(simde__m128i *)&conj256); const simde__m128i re = simde_mm_madd_epi16(i1, i2conj); From d5467aceb0bbb6ed6c697617b691d555dc57b324 Mon Sep 17 00:00:00 2001 From: Abdelkhalek Beraoud Date: Tue, 4 Feb 2025 13:44:50 +0000 Subject: [PATCH 13/48] Fix typos in NR_SA_Tutorial_OAI_multi_UE.md --- doc/NR_SA_Tutorial_OAI_multi_UE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/NR_SA_Tutorial_OAI_multi_UE.md b/doc/NR_SA_Tutorial_OAI_multi_UE.md index f596b9cb95..9b8e49ac1f 100644 --- a/doc/NR_SA_Tutorial_OAI_multi_UE.md +++ b/doc/NR_SA_Tutorial_OAI_multi_UE.md @@ -54,7 +54,7 @@ Important notes: sudo ./nr-uesoftmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --uicc0.imsi 001010000000001 --rfsimulator.options chanmod --rfsimulator.serveraddr 10.201.1.100 --telnetsrv --telnetsrv.listenport 9095 ``` -3. For the second UE, create the namespace ue2 (`-c1`), then execute shell inside (`-o1`, "open"): +3. For the second UE, create the namespace ue2 (`-c2`), then execute shell inside (`-o2`, "open"): ```bash sudo ./multi-ue.sh -c2 From 2d4d313b62fa75fbc8c39446c9e4b1d52e79a50e Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Wed, 8 Jan 2025 15:17:26 +0000 Subject: [PATCH 14/48] feat(nrLDPC_coding): Transform segment coding libraries into slot coding libraries by statically linking the adaptation layer and the segment coding libraries --- CMakeLists.txt | 24 ++++++++---- openair1/PHY/CODING/CMakeLists.txt | 38 ++++++++++++++----- openair1/PHY/CODING/TESTBENCH/ldpctest.c | 2 +- .../nrLDPC_coding_segment/CMakeLists.txt | 33 +--------------- .../nrLDPC_coding_segment_decoder.c | 13 +------ .../nrLDPC_coding_segment_encoder.c | 2 +- .../nrLDPC_coding_xdma/CMakeLists.txt | 13 ++----- .../nrLDPC_coding_xdma/nrLDPC_coding_xdma.c | 5 --- .../nrLDPC_coding_segment => }/nrLDPC_load.c | 0 9 files changed, 53 insertions(+), 77 deletions(-) rename openair1/PHY/CODING/{nrLDPC_coding/nrLDPC_coding_segment => }/nrLDPC_load.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a55506b59..ab6e988939 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -777,6 +777,8 @@ set(PHY_NRLDPC_CODINGIF add_library(dfts MODULE ${OPENAIR1_DIR}/PHY/TOOLS/oai_dfts.c ${OPENAIR1_DIR}/PHY/TOOLS/oai_dfts_neon.c) +add_library(crc_byte OBJECT ${OPENAIR1_DIR}/PHY/CODING/crc_byte.c) + set(PHY_SRC_COMMON ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/dci_tools_common.c ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/lte_mcs.c @@ -802,7 +804,6 @@ set(PHY_SRC_COMMON ${OPENAIR1_DIR}/PHY/CODING/ccoding_byte.c ${OPENAIR1_DIR}/PHY/CODING/ccoding_byte_lte.c ${OPENAIR1_DIR}/PHY/CODING/3gpplte_sse.c - ${OPENAIR1_DIR}/PHY/CODING/crc_byte.c ${PHY_TURBOIF} ${OPENAIR1_DIR}/PHY/CODING/lte_rate_matching.c ${OPENAIR1_DIR}/PHY/CODING/viterbi.c @@ -1019,9 +1020,11 @@ if (${SMBV}) endif (${SMBV}) add_library(PHY_COMMON ${PHY_SRC_COMMON}) -target_link_libraries(PHY_COMMON PRIVATE shlib_loader) +target_link_libraries(PHY_COMMON + PRIVATE shlib_loader asn1_lte_rrc_hdrs crc_byte + PUBLIC UTIL +) add_dependencies(PHY_COMMON dfts) -target_link_libraries(PHY_COMMON PRIVATE asn1_lte_rrc_hdrs PUBLIC UTIL) add_library(PHY ${PHY_SRC}) target_link_libraries(PHY PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) @@ -1954,12 +1957,17 @@ target_link_libraries(smallblocktest PRIVATE add_executable(ldpctest - ${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/ldpctest.c - ) + ${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/ldpctest.c + ${OPENAIR1_DIR}/PHY/CODING/nrLDPC_load.c +) target_link_libraries(ldpctest PRIVATE -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON -Wl,--end-group - m pthread dl shlib_loader ${T_LIB} nr_coding_segment_utils - ) + m pthread dl shlib_loader ${T_LIB} + # link 'check_crc' to make it resolved in the LDPC coding libraries + # 'check_crc' is not used in ldpctest so it is not linked in the executable by default + # --whole-archive links 'check_crc' in the executable even though it is note used, see 'man ld' + -Wl,--whole-archive crc_byte -Wl,--no-whole-archive +) add_executable(nr_dlschsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/dlschsim.c @@ -2113,7 +2121,7 @@ if (${T_TRACER}) PHY_COMMON PHY PHY_UE PHY_NR PHY_NR_COMMON PHY_NR_UE PHY_RU L2 L2_LTE L2_NR L2_LTE_NR L2_UE NR_L2_UE L2_UE_LTE_NR MAC_NR_COMMON MAC_UE_NR ngap CN_UTILS GTPV1U SCTP_CLIENT MME_APP LIB_NAS_UE NB_IoT SIMU OPENAIR0_LIB - dfts config_internals nr_common) + dfts config_internals nr_common crc_byte) if (TARGET ${i}) add_dependencies(${i} generate_T) endif() diff --git a/openair1/PHY/CODING/CMakeLists.txt b/openair1/PHY/CODING/CMakeLists.txt index c469a2b236..e4e8d43a95 100644 --- a/openair1/PHY/CODING/CMakeLists.txt +++ b/openair1/PHY/CODING/CMakeLists.txt @@ -13,28 +13,36 @@ add_library(ldpc_orig MODULE nrLDPC_encoder/ldpc_encoder.c ) set_target_properties(ldpc_orig PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -target_link_libraries(ldpc_orig PRIVATE ldpc_gen_HEADERS) +target_link_libraries(ldpc_orig PRIVATE ldpc_segment ldpc_gen_HEADERS) add_library(ldpc_optim MODULE nrLDPC_decoder/nrLDPC_decoder.c nrLDPC_encoder/ldpc_encoder_optim.c ) set_target_properties(ldpc_optim PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -target_link_libraries(ldpc_optim PRIVATE ldpc_gen_HEADERS) +target_link_libraries(ldpc_optim PRIVATE ldpc_segment ldpc_gen_HEADERS) add_library(ldpc_optim8seg MODULE nrLDPC_decoder/nrLDPC_decoder.c nrLDPC_encoder/ldpc_encoder_optim8seg.c ) set_target_properties(ldpc_optim8seg PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -target_link_libraries(ldpc_optim8seg PRIVATE ldpc_gen_HEADERS) +target_link_libraries(ldpc_optim8seg PRIVATE ldpc_segment ldpc_gen_HEADERS) -add_library(ldpc_optim8segmulti MODULE +add_library(ldpc MODULE nrLDPC_decoder/nrLDPC_decoder.c nrLDPC_encoder/ldpc_encoder_optim8segmulti.c ) -set_target_properties(ldpc_optim8segmulti PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -target_link_libraries(ldpc_optim8segmulti PRIVATE ldpc_gen_HEADERS) +set_target_properties(ldpc PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +target_link_libraries(ldpc PRIVATE ldpc_segment ldpc_gen_HEADERS) + +add_dependencies(ldpctest ldpc ldpc_orig ldpc_optim ldpc_optim8seg) +add_dependencies(nr-softmodem ldpc ldpc_orig ldpc_optim ldpc_optim8seg) +add_dependencies(nr-uesoftmodem ldpc ldpc_orig ldpc_optim ldpc_optim8seg) +add_dependencies(nr_ulsim ldpc ldpc_orig ldpc_optim ldpc_optim8seg) +add_dependencies(nr_ulschsim ldpc ldpc_orig ldpc_optim ldpc_optim8seg) +add_dependencies(nr_dlsim ldpc ldpc_orig ldpc_optim ldpc_optim8seg) +add_dependencies(nr_dlschsim ldpc ldpc_orig ldpc_optim ldpc_optim8seg) add_custom_target(nrLDPC_decoder_kernels_CL COMMAND gcc nrLDPC_decoder/nrLDPC_decoder_CL.c -dD -DNRLDPC_KERNEL_SOURCE -E -o ${CMAKE_CURRENT_BINARY_DIR}/nrLDPC_decoder_kernels_CL.clc @@ -46,7 +54,7 @@ add_library(ldpc_cl MODULE nrLDPC_encoder/ldpc_encoder_optim8segmulti.c ) set_target_properties(ldpc_cl PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -target_link_libraries(ldpc_cl PRIVATE OpenCL) +target_link_libraries(ldpc_cl PRIVATE ldpc_segment OpenCL) add_dependencies(ldpc_cl nrLDPC_decoder_kernels_CL) ############################################## @@ -64,14 +72,24 @@ if (ENABLE_LDPC_CUDA) cuda_add_library(ldpc_cuda MODULE nrLDPC_decoder_LYC/nrLDPC_decoder_LYC.cu nrLDPC_encoder/ldpc_encoder_optim8segmulti.c + # The slot coding layer cannot be linked with + # target_link_libraries like above + # because of cuda_add_library + # which already uses target_link_libraries + nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c + nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c + nrLDPC_coding/nrLDPC_coding_segment/nr_rate_matching.c ) set_target_properties(ldpc_cuda PROPERTIES CUDA_SEPARABLE_COMPILATION ON) set_target_properties(ldpc_cuda PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -endif() -add_dependencies(ldpctest ldpc_orig ldpc_optim ldpc_optim8seg ldpc_optim8segmulti) -if (ENABLE_LDPC_CUDA) add_dependencies(ldpctest ldpc_cuda) + add_dependencies(nr-softmodem ldpc_cuda) + add_dependencies(nr-uesoftmodem ldpc_cuda) + add_dependencies(nr_ulsim ldpc_cuda) + add_dependencies(nr_ulschsim ldpc_cuda) + add_dependencies(nr_dlsim ldpc_cuda) + add_dependencies(nr_dlschsim ldpc_cuda) endif() add_subdirectory(nrLDPC_coding) diff --git a/openair1/PHY/CODING/TESTBENCH/ldpctest.c b/openair1/PHY/CODING/TESTBENCH/ldpctest.c index 71c2f1af27..0fa81f360b 100644 --- a/openair1/PHY/CODING/TESTBENCH/ldpctest.c +++ b/openair1/PHY/CODING/TESTBENCH/ldpctest.c @@ -403,7 +403,7 @@ int main(int argc, char *argv[]) { short block_length=8448; // decoder supports length: 1201 -> 1280, 2401 -> 2560 // default to check output inside ldpc, the NR version checks the outer CRC defined by 3GPP - char *ldpc_version = "_optim8segmulti"; + char *ldpc_version = ""; /* version of the ldpc decoder library to use (XXX suffix to use when loading libldpc_XXX.so */ short max_iterations=5; int n_segments=1; diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/CMakeLists.txt index ed8de90594..f96a2cfaca 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/CMakeLists.txt +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/CMakeLists.txt @@ -1,39 +1,10 @@ -add_library(nr_coding_segment_utils OBJECT - nrLDPC_load.c +add_library(ldpc_segment OBJECT nr_rate_matching.c -) - -add_library(nr_coding_segment_decoder OBJECT nrLDPC_coding_segment_decoder.c -) - -add_library(nr_coding_segment_encoder OBJECT nrLDPC_coding_segment_encoder.c ) #ensure that the T header files are generated before targets depending on them if (${T_TRACER}) - add_dependencies(nr_coding_segment_utils generate_T) - add_dependencies(nr_coding_segment_decoder generate_T) - add_dependencies(nr_coding_segment_encoder generate_T) + add_dependencies(ldpc_segment generate_T) endif (${T_TRACER}) - -add_library(ldpc MODULE) -target_link_libraries(ldpc PRIVATE - nr_coding_segment_decoder - nr_coding_segment_encoder - nr_coding_segment_utils -) -set_target_properties(ldpc PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - -add_dependencies(nr-softmodem ldpc) -add_dependencies(nr-uesoftmodem ldpc) -add_dependencies(nr_ulsim ldpc) -add_dependencies(nr_ulschsim ldpc) -add_dependencies(nr_dlsim ldpc) -add_dependencies(nr_dlschsim ldpc) - -add_dependencies(ldpc ldpc_orig ldpc_optim ldpc_optim8seg ldpc_optim8segmulti) -if (ENABLE_LDPC_CUDA) - add_dependencies(ldpc ldpc_cuda) -endif() diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c index 3d228da008..9b36197e45 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c @@ -120,9 +120,6 @@ typedef struct nrLDPC_decoding_parameters_s { time_stats_t *p_ts_ldpc_decode; } nrLDPC_decoding_parameters_t; -// Global var to limit the rework of the dirty legacy code -ldpc_interface_t ldpc_interface_segment; - static void nr_process_decode_segment(void *arg) { nrLDPC_decoding_parameters_t *rdata = (nrLDPC_decoding_parameters_t *)arg; @@ -219,7 +216,7 @@ static void nr_process_decode_segment(void *arg) ////////////////////////////////// pl =====> llrProcBuf ////////////////////////////////// int decodeIterations = - ldpc_interface_segment.LDPCdecoder(p_decoderParms, 0, 0, 0, l, llrProcBuf, p_procTime, rdata->abort_decode); + LDPCdecoder(p_decoderParms, 0, 0, 0, l, llrProcBuf, p_procTime, rdata->abort_decode); if (decodeIterations <= p_decoderParms->numMaxIter) { memcpy(rdata->c, llrProcBuf, K >> 3); @@ -285,19 +282,11 @@ int nrLDPC_prepare_TB_decoding(nrLDPC_slot_decoding_parameters_t *nrLDPC_slot_de int32_t nrLDPC_coding_init(void) { - char *segment_shlibversion = NULL; - paramdef_t LoaderParams[] = { - {"segment_shlibversion", NULL, 0, .strptr = &segment_shlibversion, .defstrval = "_optim8segmulti", TYPE_STRING, 0, NULL}}; - config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_segment"); - load_LDPClib(segment_shlibversion, &ldpc_interface_segment); - return 0; } int32_t nrLDPC_coding_shutdown(void) { - free_LDPClib(&ldpc_interface_segment); - return 0; } diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c index adf9dd6624..95141e8845 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c @@ -73,7 +73,7 @@ static void ldpc8blocks_coding_segment(void *p) for (int r = 0; r < nrLDPC_TB_encoding_parameters->C; r++) c[r] = nrLDPC_TB_encoding_parameters->segments[r].c; start_meas(&nrLDPC_TB_encoding_parameters->segments[impp->macro_num * 8].ts_ldpc_encode); - ldpc_interface_segment.LDPCencoder(c, d, impp); + LDPCencoder(c, d, impp); stop_meas(&nrLDPC_TB_encoding_parameters->segments[impp->macro_num * 8].ts_ldpc_encode); // Compute where to place in output buffer that is concatenation of all segments diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/CMakeLists.txt b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/CMakeLists.txt index f51eb9d8ca..0325858389 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/CMakeLists.txt +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/CMakeLists.txt @@ -10,6 +10,9 @@ if (ENABLE_LDPC_XDMA) add_library(ldpc_xdma MODULE nrLDPC_coding_xdma_offload.c nrLDPC_coding_xdma.c + ../nrLDPC_coding_segment/nr_rate_matching.c + ../nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c + ../../nrLDPC_encoder/ldpc_encoder_optim8segmulti.c ) target_include_directories(ldpc_xdma PRIVATE ../nrLDPC_coding_segment) @@ -18,10 +21,7 @@ if (ENABLE_LDPC_XDMA) add_dependencies(ldpc_xdma generate_T) endif (${T_TRACER}) - target_link_libraries(ldpc_xdma PRIVATE - nr_coding_segment_encoder - nr_coding_segment_utils - ) + target_link_libraries(ldpc_xdma PRIVATE ldpc_gen_HEADERS) set_target_properties(ldpc_xdma PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) add_dependencies(nr-softmodem ldpc_xdma) @@ -31,9 +31,4 @@ if (ENABLE_LDPC_XDMA) add_dependencies(nr_dlsim ldpc_xdma) add_dependencies(nr_dlschsim ldpc_xdma) - add_dependencies(ldpc_xdma ldpc_orig ldpc_optim ldpc_optim8seg ldpc_optim8segmulti) - if (ENABLE_LDPC_CUDA) - add_dependencies(ldpc_xdma ldpc_cuda) - endif() - endif() diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c index 5df0bd4406..7960215ab7 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c @@ -62,7 +62,6 @@ #include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h" // Global var to limit the rework of the dirty legacy code -ldpc_interface_t ldpc_interface_segment; int num_threads_prepare_max = 0; char *user_device = NULL; char *enc_read_device = NULL; @@ -98,10 +97,8 @@ void nr_ulsch_FPGA_decoding_prepare_blocks(void *args); int32_t nrLDPC_coding_init(void) { - char *encoder_shlibversion = NULL; paramdef_t LoaderParams[] = { {"num_threads_prepare", NULL, 0, .iptr = &num_threads_prepare_max, .defintval = 0, TYPE_INT, 0, NULL}, - {"encoder_shlibversion", NULL, 0, .strptr = &encoder_shlibversion, .defstrval = "_optim8segmulti", TYPE_STRING, 0, NULL}, {"user_device", NULL, 0, .strptr = &user_device, .defstrval = DEVICE_NAME_DEFAULT_USER, TYPE_STRING, 0, NULL}, {"enc_read_device", NULL, 0, .strptr = &enc_read_device, .defstrval = DEVICE_NAME_DEFAULT_ENC_READ, TYPE_STRING, 0, NULL}, {"enc_write_device", NULL, 0, .strptr = &enc_write_device, .defstrval = DEVICE_NAME_DEFAULT_ENC_WRITE, TYPE_STRING, 0, NULL}, @@ -110,13 +107,11 @@ int32_t nrLDPC_coding_init(void) config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_xdma"); AssertFatal(num_threads_prepare_max != 0, "nrLDPC_coding_xdma.num_threads_prepare was not provided"); - load_LDPClib(encoder_shlibversion, &ldpc_interface_segment); return 0; } int32_t nrLDPC_coding_shutdown(void) { - free_LDPClib(&ldpc_interface_segment); return 0; } diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_load.c b/openair1/PHY/CODING/nrLDPC_load.c similarity index 100% rename from openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_load.c rename to openair1/PHY/CODING/nrLDPC_load.c From 6138e33f42641896247dbb2fbc774c31710dc1c6 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Tue, 14 Jan 2025 15:52:34 +0000 Subject: [PATCH 15/48] fix(nrLDPC_coding): Documentation --- openair1/PHY/CODING/DOC/LDPCImplementation.md | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/openair1/PHY/CODING/DOC/LDPCImplementation.md b/openair1/PHY/CODING/DOC/LDPCImplementation.md index cf5fb506c8..2880c45ecf 100644 --- a/openair1/PHY/CODING/DOC/LDPCImplementation.md +++ b/openair1/PHY/CODING/DOC/LDPCImplementation.md @@ -28,9 +28,6 @@ shlib_path libldpc.so `libldpc.so` has its decoder implemented in [nrLDPC_coding_segment_decoder.c](file://../nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c).\ Its encoder is implemented in [nrLDPC_coding_segment_encoder.c](file://../nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c). -*Note: The segment coding library `libldpc.so` is an adaptation layer between the slot coding interface and a segment coding library.* -*The segment coding library is `libldpc_optim8segmulti.so` by default but it can be chosen with option `--nrLDPC_coding_segment.segment_shlibversion` followed by the library version - like with `--loader.ldpc.shlibversion` in the slot coding case -* - loading `libldpc_t2.so` instead of `libldpc.so`: `make ldpc_t2` @@ -114,28 +111,29 @@ Libraries implementing the slotwise LDPC coding must be named `libldpc<_version> The interface of the library is defined in [nrLDPC_defs.h](file://../nrLDPC_defs.h). The code loading the LDPC library is in [nrLDPC_load.c](file://../nrLDPC_load.c), in function `load_nrLDPClib`, which must be called at init time. -*Note: LDPC segment coding libraries are not loaded directly by OAI but by the intermediate library `libldpc.so` which is implementing the LDPC slot coding interface using a LDPC segment coding library.* +LDPC segment coding libraries are not loaded directly by OAI but are statically linked in libraries implementing the LDPC slot coding interface using an adaptation layer between the slot coding interface and the segment coding interface. +This way, these libraries can be loaded to implement the slot coding interface as well as the segment coding interface in `ldpctest`. ### Selecting the LDPC library at run time -By default the function `int load_nrLDPClib(void)` looks for `libldpc_optim8segmulti.so`, this default behavior can be changed using the oai loader configuration options in the configuration file or from the command line as shown below: +By default the function `int load_nrLDPClib(void)` looks for `libldpc.so`, this default behavior can be changed using the oai loader configuration options in the configuration file or from the command line as shown below: #### Examples of ldpc shared lib selection when running nr softmodem's: -loading `libldpc_optim8seg.so` instead of `libldpc_optim8segmulti.so`: +loading `libldpc_optim8seg.so` instead of `libldpc.so`: ``` -./nr-softmodem -O libconfig:gnb.band78.tm1.106PRB.usrpx300.conf:dbgl5 --nrLDPC_coding_segment.segment_shlibversion _optim8seg +./nr-softmodem -O libconfig:gnb.band78.tm1.106PRB.usrpx300.conf:dbgl5 --loader.ldpc.shlibversion _optim8seg ....................... -[CONFIG] nrLDPC_coding_segment.segment_shlibversion set to default value "_optim8segmulti" -[LIBCONFIG] nrLDPC_coding_segment: 1/1 parameters successfully set, (0 to default value) -[CONFIG] segment_shlibversion set to _optim8seg from command line -[CONFIG] nrLDPC_coding_segment 1 options set from command line +[CONFIG] loader.ldpc.shlibversion set to default value "" +[LIBCONFIG] loader.ldpc: 2/2 parameters successfully set, (1 to default value) +[CONFIG] shlibversion set to _optim8seg from command line +[CONFIG] loader.ldpc 1 options set from command line [LOADER] library libldpc_optim8seg.so successfully loaded ........................ ``` -loading `libldpc_cl.so` instead of `libldpc_optim8segmulti.so`: +loading `libldpc_cl.so` instead of `libldpc.so`: `make ldpc_cl` @@ -161,7 +159,7 @@ Built target ldpc_cl At runtime, to successfully use hardware acceleration via OpenCL, you need to install vendor specific packages which deliver the required drivers and tools to make use of their GPU (Nvidia, Intel...) , fpga (Xilinx, Intel) or CPU (Intel, AMD, ARM...) through OpenCL. -`./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --nrLDPC_coding_segment.segment_shlibversion _cl` +`./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _cl` ``` ------------------------------------------------ @@ -196,12 +194,12 @@ At runtime, to successfully use hardware acceleration via OpenCL, you need to in ----------------------------------------------------------------- ``` -`./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim -O libconfig:/usr/local/oai/conf/nrue_sim.conf:dbgl5 --nrLDPC_coding_segment.segment_shlibversion _cl --log_config.hw_log_level info` +`./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim -O libconfig:/usr/local/oai/conf/nrue_sim.conf:dbgl5 --loader.ldpc.shlibversion _cl --log_config.hw_log_level info` ``` ............................................................ [CONFIG] segment_shlibversion set to _cl from command line -[CONFIG] nrLDPC_coding_segment 1 options set from command line +[CONFIG] loader.ldpc 1 options set from command line [LOADER] library libldpc_cl.so successfully loaded [HW] Platform 0, OpenCL profile FULL_PROFILE [HW] Platform 0, OpenCL version OpenCL 2.1 LINUX @@ -254,7 +252,7 @@ SNR0 -2.000000: [CONFIG] log_config: 16/16 parameters successfully set log init done [CONFIG] loader: 2/2 parameters successfully set -[CONFIG] loader.ldpc: 1/2 parameters successfully set +[CONFIG] loader.ldpc: 2/2 parameters successfully set [LOADER] library libldpc_cuda.so successfully loaded ................................... ``` From fabaae3327569b3eb831728a12c4d2b24e40e0f4 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 5 Feb 2025 14:07:59 +0100 Subject: [PATCH 16/48] removing initial sync option from dlsim --- openair1/SIMULATION/NR_PHY/dlsim.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 885684fe6e..e3321a93ad 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -325,7 +325,6 @@ int main(int argc, char **argv) gNB_MAC_INST *gNB_mac; NR_UE_MAC_INST_t *UE_mac; int cyclic_prefix_type = NFAPI_CP_NORMAL; - int run_initial_sync=0; int loglvl=OAILOG_WARNING; //float target_error_rate = 0.01; @@ -536,11 +535,6 @@ int main(int argc, char **argv) gNBthreads[sizeof(gNBthreads)-1]=0; break; - case 'Y': - run_initial_sync = 1; - slot = 0; - break; - case 'Z' : filename_csv = strdup(optarg); AssertFatal(filename_csv != NULL, "strdup() error: errno %d\n", errno); @@ -551,7 +545,6 @@ int main(int argc, char **argv) break; case 'H': - AssertFatal(run_initial_sync == 0, "Cannot use Y and H options at the same time\n"); slot = atoi(optarg); break; @@ -611,13 +604,12 @@ int main(int argc, char **argv) printf("-T Enable PTRS, arguments list L_PTRS{0,1,2} K_PTRS{2,4}, e.g. -T 2 0 2 \n"); printf("-U Change DMRS Config, arguments list DMRS TYPE{0=A,1=B} DMRS AddPos{0:2} DMRS ConfType{1:2}, e.g. -U 3 0 2 1 \n"); printf("-X gNB thread pool configuration, n => no threads\n"); - printf("-Y Run initial sync in UE\n"); printf("-Z Output filename (.csv format) for stats\n"); exit (-1); break; } } - +printf("%d\n", slot); logInit(); set_glog(loglvl); /* initialize the sin table */ @@ -854,11 +846,7 @@ int main(int argc, char **argv) UE->nrLDPC_coding_interface = gNB->nrLDPC_coding_interface; UE->max_ldpc_iterations = max_ldpc_iterations; init_nr_ue_phy_cpu_stats(&UE->phy_cpu_stats); - - if (run_initial_sync==1) - UE->is_synchronized = 0; - else - UE->is_synchronized = 1; + UE->is_synchronized = 1; if (init_nr_ue_signal(UE, 1) != 0) { @@ -1059,15 +1047,13 @@ int main(int argc, char **argv) msgDataTx->ssb[0].ssb_pdu.ssb_pdu_rel15.bchPayload=0x001234; msgDataTx->ssb[0].ssb_pdu.ssb_pdu_rel15.SsbBlockIndex = 0; msgDataTx->gNB = gNB; - if (run_initial_sync) { - nr_common_signal_procedures(gNB,frame,slot,msgDataTx->ssb[0].ssb_pdu); - } else { - start_meas(&gNB->phy_proc_tx); - phy_procedures_gNB_TX(msgDataTx,frame,slot,1); - stop_meas(&gNB->phy_proc_tx); - } + + start_meas(&gNB->phy_proc_tx); + phy_procedures_gNB_TX(msgDataTx,frame,slot,1); + stop_meas(&gNB->phy_proc_tx); + int txdataF_offset = slot * frame_parms->samples_per_slot_wCP; - + if (n_trials==1) { LOG_M("txsigF0.m","txsF0=", &gNB->common_vars.txdataF[0][0][txdataF_offset +2 * frame_parms->ofdm_symbol_size], From bd179fd5e452156ebd2c8197d4ba278392b36f1d Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 5 Feb 2025 16:30:26 +0100 Subject: [PATCH 17/48] making phy-test slot bitmap tdd period based to minimize the chance to need it larger than 64 slots --- ci-scripts/xml_files/gnb_phytest_usrp_run.xml | 2 +- .../gnb_phytest_usrp_run_100_2x2.xml | 2 +- .../xml_files/gnb_phytest_usrp_run_60.xml | 2 +- .../xml_files/gnb_phytest_usrp_run_60_2x2.xml | 2 +- executables/nr-softmodem-common.h | 4 +- .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 42 +++++++++---------- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 4 +- 7 files changed, 26 insertions(+), 32 deletions(-) diff --git a/ci-scripts/xml_files/gnb_phytest_usrp_run.xml b/ci-scripts/xml_files/gnb_phytest_usrp_run.xml index 88f5079b68..7a66ea6c34 100644 --- a/ci-scripts/xml_files/gnb_phytest_usrp_run.xml +++ b/ci-scripts/xml_files/gnb_phytest_usrp_run.xml @@ -45,7 +45,7 @@ Initialize_eNB Initialize gNB USRP - -O ci-scripts/conf_files/gnb.band78.106prb.usrpn300.phytest-dora.conf --phy-test -q -U 787200 -T 106 -t 23 -D 130175 -m 28 -M 106 --usrp-tx-thread-config 1 --log_config.global_log_options level,nocolor,time + -O ci-scripts/conf_files/gnb.band78.106prb.usrpn300.phytest-dora.conf --phy-test -q -U 768 -T 106 -t 23 -D 127 -m 28 -M 106 --usrp-tx-thread-config 1 --log_config.global_log_options level,nocolor,time datalog_rt_stats.default.yaml NR 192.168.20.2 diff --git a/ci-scripts/xml_files/gnb_phytest_usrp_run_100_2x2.xml b/ci-scripts/xml_files/gnb_phytest_usrp_run_100_2x2.xml index f81d747bfe..2f94fa986d 100644 --- a/ci-scripts/xml_files/gnb_phytest_usrp_run_100_2x2.xml +++ b/ci-scripts/xml_files/gnb_phytest_usrp_run_100_2x2.xml @@ -45,7 +45,7 @@ Initialize_eNB Initialize gNB USRP - -O ci-scripts/conf_files/gnb.band78.273prb.usrpn300.phytest-dora.conf --phy-test -q -U 787200 -T 273 -t 23 -D 130175 -m 23 -M 273 -l 2 --usrp-tx-thread-config 1 --log_config.global_log_options level,nocolor,time + -O ci-scripts/conf_files/gnb.band78.273prb.usrpn300.phytest-dora.conf --phy-test -q -U 768 -T 273 -t 23 -D 127 -m 23 -M 273 -l 2 --usrp-tx-thread-config 1 --log_config.global_log_options level,nocolor,time datalog_rt_stats.100.2x2.yaml NR 192.168.20.2 diff --git a/ci-scripts/xml_files/gnb_phytest_usrp_run_60.xml b/ci-scripts/xml_files/gnb_phytest_usrp_run_60.xml index 4b0fec92e2..77e47c66b4 100644 --- a/ci-scripts/xml_files/gnb_phytest_usrp_run_60.xml +++ b/ci-scripts/xml_files/gnb_phytest_usrp_run_60.xml @@ -45,7 +45,7 @@ Initialize_eNB Initialize gNB USRP - -O ci-scripts/conf_files/gnb.band78.162prb.usrpn300.phytest-dora.conf --phy-test -q -U 787200 -T 162 -t 23 -D 130175 -m 23 -M 162 --usrp-tx-thread-config 1 --log_config.global_log_options level,nocolor,time + -O ci-scripts/conf_files/gnb.band78.162prb.usrpn300.phytest-dora.conf --phy-test -q -U 768 -T 162 -t 23 -D 127 -m 23 -M 162 --usrp-tx-thread-config 1 --log_config.global_log_options level,nocolor,time datalog_rt_stats.1x1.60.yaml NR 192.168.20.2 diff --git a/ci-scripts/xml_files/gnb_phytest_usrp_run_60_2x2.xml b/ci-scripts/xml_files/gnb_phytest_usrp_run_60_2x2.xml index f9e298127f..67e2316ea3 100644 --- a/ci-scripts/xml_files/gnb_phytest_usrp_run_60_2x2.xml +++ b/ci-scripts/xml_files/gnb_phytest_usrp_run_60_2x2.xml @@ -45,7 +45,7 @@ Initialize_eNB Initialize gNB USRP - -O ci-scripts/conf_files/gnb.band78.162prb.usrpn300.phytest-dora.conf --phy-test --gNBs.[0].pdsch_AntennaPorts_XP 2 --RUs.[0].nb_tx 2 --RUs.[0].nb_rx 2 -q -U 787200 -T 162 -t 23 -D 130175 -m 23 -M 162 -l 2 --usrp-tx-thread-config 1 --log_config.global_log_options level,nocolor,time + -O ci-scripts/conf_files/gnb.band78.162prb.usrpn300.phytest-dora.conf --phy-test --gNBs.[0].pdsch_AntennaPorts_XP 2 --RUs.[0].nb_tx 2 --RUs.[0].nb_rx 2 -q -U 768 -T 162 -t 23 -D 127 -m 23 -M 162 -l 2 --usrp-tx-thread-config 1 --log_config.global_log_options level,nocolor,time datalog_rt_stats.60.2x2.yaml NR 192.168.20.2 diff --git a/executables/nr-softmodem-common.h b/executables/nr-softmodem-common.h index b7fc576845..3ab6d1e73d 100644 --- a/executables/nr-softmodem-common.h +++ b/executables/nr-softmodem-common.h @@ -64,8 +64,8 @@ #define CONFIG_HLP_DLBW_PHYTEST "Set the number of PRBs used for DLSCH in PHYTEST mode\n" #define CONFIG_HLP_ULBW_PHYTEST "Set the number of PRBs used for ULSCH in PHYTEST mode\n" #define CONFIG_HLP_PRB_SA "Set the number of PRBs for SA\n" -#define CONFIG_HLP_DLBM_PHYTEST "Bitmap for DLSCH slots (slot 0 starts at LSB)\n" -#define CONFIG_HLP_ULBM_PHYTEST "Bitmap for ULSCH slots (slot 0 starts at LSB)\n" +#define CONFIG_HLP_DLBM_PHYTEST "Bitmap for DLSCH slots in period (slot 0 starts at LSB)\n" +#define CONFIG_HLP_ULBM_PHYTEST "Bitmap for ULSCH slots in period (slot 0 starts at LSB)\n" #define CONFIG_HLP_SSC "Set the start subcarrier \n" #define CONFIG_HLP_TDD "Set hardware to TDD mode (default: FDD). Used only with -U (otherwise set in config file).\n" #define CONFIG_HLP_UE "Set the lte softmodem as a UE\n" diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index f2c014a65f..989aa1be9a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -41,27 +41,29 @@ // #define ENABLE_MAC_PAYLOAD_DEBUG 1 /* This function checks whether the given Dl/UL slot is set - in the input bitmap, which is a mask indicating in which + in the input bitmap (per period), which is a mask indicating in which slot to transmit (among those available in the TDD configuration) */ static bool is_xlsch_in_slot(uint64_t bitmap, sub_frame_t slot) { - return (bitmap >> (slot % 64)) & 0x01; + AssertFatal(slot < 64, "Unable to handle periods with length larger than 64 slots in phy-test mode\n"); + return (bitmap >> slot) & 0x01; } uint32_t target_dl_mcs = 9; uint32_t target_dl_Nl = 1; uint32_t target_dl_bw = 50; uint64_t dlsch_slot_bitmap = (1<<1); + /* schedules whole bandwidth for first user, all the time */ -void nr_preprocessor_phytest(module_id_t module_id, - frame_t frame, - sub_frame_t slot) +void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_t slot) { + gNB_MAC_INST *mac = RC.nrmac[module_id]; /* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */ - if (!is_xlsch_in_slot(dlsch_slot_bitmap, slot)) + int slot_period = slot % mac->frame_structure.numb_slots_period; + if (!is_xlsch_in_slot(dlsch_slot_bitmap, slot_period)) return; - NR_UE_info_t *UE = RC.nrmac[module_id]->UE_info.list[0]; - NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon; + NR_UE_info_t *UE = mac->UE_info.list[0]; + NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP; const int CC_id = 0; @@ -72,7 +74,7 @@ void nr_preprocessor_phytest(module_id_t module_id, return; } - const int tda = get_dl_tda(RC.nrmac[module_id], slot); + const int tda = get_dl_tda(mac, slot); NR_tda_info_t tda_info = get_dl_tda_info(dl_bwp, sched_ctrl->search_space->searchSpaceType->present, tda, @@ -98,7 +100,7 @@ void nr_preprocessor_phytest(module_id_t module_id, int rbSize = 0; if (target_dl_bw>bwpSize) target_dl_bw = bwpSize; - uint16_t *vrb_map = RC.nrmac[module_id]->common_channels[CC_id].vrb_map[beam]; + uint16_t *vrb_map = mac->common_channels[CC_id].vrb_map[beam]; /* loop ensures that we allocate exactly target_dl_bw, or return */ while (true) { /* advance to first free RB */ @@ -139,7 +141,7 @@ void nr_preprocessor_phytest(module_id_t module_id, 0); sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer; - int CCEIndex = get_cce_index(RC.nrmac[module_id], + int CCEIndex = get_cce_index(mac, CC_id, slot, UE->rnti, &sched_ctrl->aggregation_level, beam, @@ -147,10 +149,7 @@ void nr_preprocessor_phytest(module_id_t module_id, sched_ctrl->coreset, &sched_ctrl->sched_pdcch, false); - AssertFatal(CCEIndex >= 0, - "%s(): could not find CCE for UE %04x\n", - __func__, - UE->rnti); + AssertFatal(CCEIndex >= 0, "Could not find CCE for UE %04x\n", UE->rnti); NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch; if (sched_pdsch->dl_harq_pid == -1) @@ -159,20 +158,16 @@ void nr_preprocessor_phytest(module_id_t module_id, int alloc = -1; if (!get_FeedbackDisabled(UE->sc_info.downlinkHARQ_FeedbackDisabled_r17, sched_pdsch->dl_harq_pid)) { int r_pucch = nr_get_pucch_resource(sched_ctrl->coreset, UE->current_UL_BWP.pucch_Config, CCEIndex); - alloc = nr_acknack_scheduling(RC.nrmac[module_id], UE, frame, slot, 0, r_pucch, 0); + alloc = nr_acknack_scheduling(mac, UE, frame, slot, 0, r_pucch, 0); if (alloc < 0) { - LOG_D(MAC, - "Could not find PUCCH for UE %04x@%d.%d\n", - rnti, - frame, - slot); + LOG_D(NR_MAC, "Could not find PUCCH for UE %04x@%d.%d\n", rnti, frame, slot); return; } } sched_ctrl->cce_index = CCEIndex; - fill_pdcch_vrb_map(RC.nrmac[module_id], + fill_pdcch_vrb_map(mac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, @@ -270,7 +265,8 @@ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_ /* check if slot is UL, and that slot is 8 (assuming K2=6 because of UE * limitations). Note that if K2 or the TDD configuration is changed, below * conditions might exclude each other and never be true */ - if (!is_xlsch_in_slot(ulsch_slot_bitmap, sched_slot)) + int slot_period = sched_slot % nr_mac->frame_structure.numb_slots_period; + if (!is_xlsch_in_slot(ulsch_slot_bitmap, slot_period)) return false; uint16_t rbStart = 0; diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 4cd5563200..b7ed84ec9a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -150,9 +150,7 @@ uint16_t nr_mac_compute_RIV(uint16_t N_RB_DL, uint16_t RBstart, uint16_t Lcrbs); /* \brief preprocessor for phytest: schedules UE_id 0 with fixed MCS on all * freq resources */ -void nr_preprocessor_phytest(module_id_t module_id, - frame_t frame, - sub_frame_t slot); +void nr_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_t slot); /* \brief UL preprocessor for phytest: schedules UE_id 0 with fixed MCS on a * fixed set of resources */ bool nr_ul_preprocessor_phytest(module_id_t module_id, frame_t frame, sub_frame_t slot); From 3e27175ac2a2c8f5a1f42ded3c6ca973be24d36a Mon Sep 17 00:00:00 2001 From: Raghavendra Dinavahi Date: Thu, 6 Feb 2025 15:36:02 +0100 Subject: [PATCH 18/48] Added Support of 35Mhz,45Mhz,70Mhz Bandwidth for SCS 30Khz Also added support for 35Mhz, 45Mhz bw for 15Khz Subcarrierspacing Referred Spec TS 38.101 v18.6.0 section 5.3.2, table 5.3.2-1 --- common/utils/nr/nr_common.c | 57 +++- .../CONF/gnb.sa.band78.fr1.189PRB.rfsim.conf | 245 ++++++++++++++++++ 2 files changed, 293 insertions(+), 9 deletions(-) create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.189PRB.rfsim.conf diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index 9b029657ad..2073eb9c38 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -188,18 +188,20 @@ uint64_t reverse_bits(uint64_t in, int n_bits) return rev_bits; } -static const int tables_5_3_2[5][12] = { - {25, 52, 79, 106, 133, 160, 216, 270, -1, -1, -1, -1}, // 15 FR1 - {11, 24, 38, 51, 65, 78, 106, 133, 162, 217, 245, 273}, // 30 FR1 - {-1, 11, 18, 24, 31, 38, 51, 65, 79, 107, 121, 135}, // 60 FR1 - {66, 132, 264, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 60 FR2 - {32, 66, 132, 264, -1, -1, -1, -1, -1, -1, -1, -1} // 120FR2 +#define NUM_BW_ENTRIES 15 + +static const int tables_5_3_2[5][NUM_BW_ENTRIES] = { + {25, 52, 79, 106, 133, 160, 188, 216, 242, 270, -1, -1, -1, -1, -1}, // 15 FR1 + {11, 24, 38, 51, 65, 78, 92, 106, 119, 133, 162, 189, 217, 245, 273}, // 30 FR1 + {-1, 11, 18, 24, 31, 38, 44, 51, 58, 65, 79, 93, 107, 121, 135}, // 60 FR1 + {66, 132, 264, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 60 FR2 + {32, 66, 132, 264, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} // 120FR2 }; int get_supported_band_index(int scs, frequency_range_t freq_range, int n_rbs) { int scs_index = scs + freq_range; - for (int i = 0; i < 12; i++) { + for (int i = 0; i < NUM_BW_ENTRIES; i++) { if(n_rbs == tables_5_3_2[scs_index][i]) return i; } @@ -209,7 +211,7 @@ int get_supported_band_index(int scs, frequency_range_t freq_range, int n_rbs) int get_smallest_supported_bandwidth_index(int scs, frequency_range_t frequency_range, int n_rbs) { int scs_index = scs + frequency_range; - for (int i = 0; i < 12; i++) { + for (int i = 0; i < NUM_BW_ENTRIES; i++) { if (n_rbs <= tables_5_3_2[scs_index][i]) return i; } @@ -422,7 +424,7 @@ void check_ssb_raster(uint64_t freq, int band, int scs) int get_supported_bw_mhz(frequency_range_t frequency_range, int bw_index) { if (frequency_range == FR1) { - int bandwidth_index_to_mhz[] = {5, 10, 15, 20, 25, 30, 40, 50, 60, 80, 90, 100}; + int bandwidth_index_to_mhz[] = {5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100}; AssertFatal(bw_index >= 0 && bw_index <= sizeofArray(bandwidth_index_to_mhz), "Bandwidth index %d is invalid\n", bw_index); @@ -826,6 +828,18 @@ void get_samplerate_and_bw(int mu, *rx_bw = 50e6; } break; + case 242: // 45Mhz + case 188: // 35Mhz + if (threequarter_fs) { + *sample_rate = 46.08e6; + *samples_per_frame = 460800; + } else { + *sample_rate = 61.44e6; + *samples_per_frame = 614400; + } + *tx_bw = (n_rb == 242) ? 45e6 : 35e6; + *rx_bw = (n_rb == 242) ? 45e6 : 35e6; + break; case 216: if (threequarter_fs) { *sample_rate=46.08e6; @@ -926,6 +940,17 @@ void get_samplerate_and_bw(int mu, *rx_bw = 80e6; } break; + case 189: + if (threequarter_fs) { + *sample_rate = 92.16e6; + *samples_per_frame = 921600; + } else { + *sample_rate = 122.88e6; + *samples_per_frame = 1228800; + } + *tx_bw = 70e6; + *rx_bw = 70e6; + break; case 162 : if (threequarter_fs) { AssertFatal(1==0,"N_RB %d cannot use 3/4 sampling\n",n_rb); @@ -951,6 +976,20 @@ void get_samplerate_and_bw(int mu, } break; + + case 119: // 45Mhz + case 92: // 35Mhz + if (threequarter_fs) { + *sample_rate = 46.08e6; + *samples_per_frame = 460800; + } else { + *sample_rate = 61.44e6; + *samples_per_frame = 614400; + } + *tx_bw = (n_rb == 119) ? 45e6 : 35e6; + *rx_bw = (n_rb == 119) ? 45e6 : 35e6; + break; + case 106: if (threequarter_fs) { *sample_rate=46.08e6; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.189PRB.rfsim.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.189PRB.rfsim.conf new file mode 100644 index 0000000000..5270587271 --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.189PRB.rfsim.conf @@ -0,0 +1,245 @@ +Active_gNBs = ( "gnb-rfsim"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + +# cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gnb-rfsim"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + + plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); + + nr_cellid = 12345678L + +# tr_s_preference = "local_mac" + + ////////// Physical parameters: + + min_rxtxtime = 6; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3300.60 MHz + 53*12*30e-3 MHz = 3319.68 + absoluteFrequencySSB = 621312; + # this is 3300.60 MHz + dl_absoluteFrequencyPointA = 620040; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 189 + #initialDownlinkBWP + #genericParameters + # this is RBstart=0,L=189 (275*(275-L+1) + 275-1-RBstart) + initialDLBWPlocationAndBandwidth = 24199; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 11; + initialDLBWPsearchSpaceZero = 0; + + + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 189; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 24199; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 12; + preambleReceivedTargetPower = -104; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 5; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#one (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; + + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + ////////// AMF parameters: + amf_ip_address = ({ ipv4 = "192.168.71.132"; }); + + NETWORK_INTERFACES : + { + + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.140"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.140"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( + { + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 200; + pucch_TargetSNRx10 = 200; + } +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 200; +# pucch0_dtx_threshold = 150; +} +); + +RUs = ( + { + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + eNB_instances = [0]; + sf_extension = 0 + sdr_addrs = "serial=XXXXXXX" + } +); + + +rfsimulator: { + serveraddr = "server"; +}; + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + + log_config : + { + global_log_level ="info"; + hw_log_level ="info"; + phy_log_level ="info"; + mac_log_level ="info"; + rlc_log_level ="info"; + pdcp_log_level ="info"; + rrc_log_level ="info"; + f1ap_log_level ="debug"; + }; From e61bf5132db2adab44414883de4b3b418247b7f3 Mon Sep 17 00:00:00 2001 From: Bartosz Podrygajlo Date: Tue, 4 Feb 2025 11:20:29 +0100 Subject: [PATCH 19/48] Refactor tun_if.h Use a common function for generating interface name. Use full interface name in tun_if.h functions. --- common/utils/tun_if.c | 95 +++++++++-------------- common/utils/tun_if.h | 43 ++++++---- openair2/LAYER2/PDCP_v10.1.0/pdcp.c | 31 +++++--- openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c | 18 ++++- openair2/RRC/LTE/rrc_UE.c | 4 +- openair2/RRC/LTE/rrc_eNB.c | 4 +- openair3/NAS/NR_UE/nr_nas_msg.c | 16 ++-- openair3/NAS/UE/ESM/esm_ebr_context.c | 6 +- 8 files changed, 123 insertions(+), 94 deletions(-) diff --git a/common/utils/tun_if.c b/common/utils/tun_if.c index 4d04325998..79fcc6a954 100644 --- a/common/utils/tun_if.c +++ b/common/utils/tun_if.c @@ -38,7 +38,7 @@ int nas_sock_fd[MAX_MOBILES_PER_ENB * 2]; // Allocated for both LTE UE and NR UE. int nas_sock_mbms_fd; -static int tun_alloc(char *dev) +static int tun_alloc(const char *dev) { struct ifreq ifr; int fd, err; @@ -55,25 +55,27 @@ static int tun_alloc(char *dev) * IFF_NO_PI - Do not provide packet information */ ifr.ifr_flags = IFF_TUN | IFF_NO_PI; - - if (*dev) - strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name) - 1); - + strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name) - 1); if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) { close(fd); return err; } + // According to https://www.kernel.org/doc/Documentation/networking/tuntap.txt, TUNSETIFF performs string + // formatting on ifr_name. To ensure that the generated name is what we expect, check it here. + AssertFatal(strcmp(ifr.ifr_name, dev) == 0, "Failed to create tun interface, expected ifname %s, got %s\n", dev, ifr.ifr_name); + + err = fcntl(fd, F_SETFL, O_NONBLOCK); + if (err == -1) { + close(fd); + LOG_E(UTIL, "Error fcntl (%d:%s)\n", errno, strerror(errno)); + return err; + } - strcpy(dev, ifr.ifr_name); return fd; } -int tun_init_mbms(char *ifprefix, int id) +int tun_init_mbms(char *ifname) { - int ret; - char ifname[64]; - - sprintf(ifname, "%s%d", ifprefix, id); nas_sock_mbms_fd = tun_alloc(ifname); if (nas_sock_mbms_fd == -1) { @@ -82,48 +84,26 @@ int tun_init_mbms(char *ifprefix, int id) } LOG_D(UTIL, "Opened socket %s with fd %d\n", ifname, nas_sock_mbms_fd); - ret = fcntl(nas_sock_mbms_fd, F_SETFL, O_NONBLOCK); - - if (ret == -1) { - LOG_E(UTIL, "Error fcntl (%d:%s)\n", errno, strerror(errno)); - return 0; - } struct sockaddr_nl nas_src_addr = {0}; nas_src_addr.nl_family = AF_NETLINK; nas_src_addr.nl_pid = 1; nas_src_addr.nl_groups = 0; /* not in mcast groups */ - ret = bind(nas_sock_mbms_fd, (struct sockaddr *)&nas_src_addr, sizeof(nas_src_addr)); + bind(nas_sock_mbms_fd, (struct sockaddr *)&nas_src_addr, sizeof(nas_src_addr)); return 1; } -int tun_init(const char *ifprefix, int num_if, int id) +int tun_init(const char *ifname, int instance_id) { - int ret; - char ifname[64]; + nas_sock_fd[instance_id] = tun_alloc(ifname); - int begx = (id == 0) ? 0 : id - 1; - int endx = (id == 0) ? num_if : id; - for (int i = begx; i < endx; i++) { - sprintf(ifname, "%s%d", ifprefix, i + 1); - nas_sock_fd[i] = tun_alloc(ifname); - - if (nas_sock_fd[i] == -1) { - LOG_E(UTIL, "Error opening socket %s (%d:%s)\n", ifname, errno, strerror(errno)); - return 0; - } - - LOG_I(UTIL, "Opened socket %s with fd nas_sock_fd[%d]=%d\n", ifname, i, nas_sock_fd[i]); - ret = fcntl(nas_sock_fd[i], F_SETFL, O_NONBLOCK); - - if (ret == -1) { - LOG_E(UTIL, "Error fcntl (%d:%s)\n", errno, strerror(errno)); - - return 0; - } + if (nas_sock_fd[instance_id] == -1) { + LOG_E(UTIL, "Error opening socket %s (%d:%s)\n", ifname, errno, strerror(errno)); + return 0; } + LOG_I(UTIL, "Opened socket %s with fd nas_sock_fd[%d]=%d\n", ifname, instance_id, nas_sock_fd[instance_id]); return 1; } @@ -209,12 +189,8 @@ fail_interface_state: return false; } -// non blocking full configuration of the interface (address, and the two lest octets of the address) -bool tun_config(int interface_id, const char *ipv4, const char *ipv6, const char *ifpref) +bool tun_config(const char* ifname, const char *ipv4, const char *ipv6) { - char interfaceName[IFNAMSIZ]; - snprintf(interfaceName, sizeof(interfaceName), "%s%d", ifpref, interface_id); - AssertFatal(ipv4 != NULL || ipv6 != NULL, "need to have IP address, but none given\n"); int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); @@ -223,13 +199,16 @@ bool tun_config(int interface_id, const char *ipv4, const char *ipv6, const char return false; } - change_interface_state(sock_fd, interfaceName, INTERFACE_DOWN); + if (!change_interface_state(sock_fd, ifname, INTERFACE_DOWN)) { + close(sock_fd); + return false; + } bool success = true; if (ipv4 != NULL) - success = setInterfaceParameter(sock_fd, interfaceName, AF_INET, ipv4, SIOCSIFADDR); + success = setInterfaceParameter(sock_fd, ifname, AF_INET, ipv4, SIOCSIFADDR); // set the machine network mask for IPv4 if (success && ipv4 != NULL) - success = setInterfaceParameter(sock_fd, interfaceName, AF_INET, "255.255.255.0", SIOCSIFNETMASK); + success = setInterfaceParameter(sock_fd, ifname, AF_INET, "255.255.255.0", SIOCSIFNETMASK); if (ipv6 != NULL) { // for setting the IPv6 address, we need an IPv6 socket. For setting IPv4, @@ -240,27 +219,25 @@ bool tun_config(int interface_id, const char *ipv4, const char *ipv6, const char LOG_E(UTIL, "Failed creating socket for interface management: %d, %s\n", errno, strerror(errno)); success = false; } - success = success && setInterfaceParameter(sock_fd, interfaceName, AF_INET6, ipv6, SIOCSIFADDR); + success = success && setInterfaceParameter(sock_fd, ifname, AF_INET6, ipv6, SIOCSIFADDR); close(sock_fd); } if (success) - success = change_interface_state(sock_fd, interfaceName, INTERFACE_UP); + success = change_interface_state(sock_fd, ifname, INTERFACE_UP); if (success) - LOG_I(OIP, "Interface %s successfully configured, IPv4 %s, IPv6 %s\n", interfaceName, ipv4, ipv6); + LOG_I(OIP, "Interface %s successfully configured, IPv4 %s, IPv6 %s\n", ifname, ipv4, ipv6); else - LOG_E(OIP, "Interface %s couldn't be configured (IPv4 %s, IPv6 %s)\n", interfaceName, ipv4, ipv6); + LOG_E(OIP, "Interface %s couldn't be configured (IPv4 %s, IPv6 %s)\n", ifname, ipv4, ipv6); close(sock_fd); return success; } -void setup_ue_ipv4_route(int interface_id, const char *ipv4, const char *ifpref) +void setup_ue_ipv4_route(const char* ifname, int instance_id, const char *ipv4) { - int table_id = interface_id - 1 + 10000; - char interfaceName[IFNAMSIZ]; - snprintf(interfaceName, sizeof(interfaceName), "%s%d", ifpref, interface_id); + int table_id = instance_id - 1 + 10000; char command_line[500]; int res = sprintf(command_line, @@ -271,7 +248,7 @@ void setup_ue_ipv4_route(int interface_id, const char *ipv4, const char *ifpref) table_id, ipv4, table_id, - interfaceName, + ifname, table_id); if (res < 0) { @@ -281,3 +258,7 @@ void setup_ue_ipv4_route(int interface_id, const char *ipv4, const char *ifpref) background_system(command_line); } +int tun_generate_ifname(char *ifname, const char *ifprefix, int instance_id) +{ + return snprintf(ifname, IFNAMSIZ, "%s%d", ifprefix, instance_id + 1); +} diff --git a/common/utils/tun_if.h b/common/utils/tun_if.h index 45ae34983f..eafc5b3350 100644 --- a/common/utils/tun_if.h +++ b/common/utils/tun_if.h @@ -23,28 +23,43 @@ #define TUN_IF_H_ #include +#include -/* TODO: doc */ -int tun_init(const char *ifprefix, int num_if, int id); +/*! + * \brief This function generates the name of the interface based on the prefix and + * the instance id. + * + * \param[in,out] ifname name of the interface + * \param[in] ifprefix prefix of the interface + * \param[in] instance_id unique instance number + */ +int tun_generate_ifname(char *ifname, const char *ifprefix, int instance_id); -/* TODO: doc */ -int tun_init_mbms(char *ifsuffix, int id); +/*! + * \brief This function initializes the TUN interface + * \param[in] ifname name of the interface + * \param[in] instance_id unique instance number, used to save socket file descriptor + */ +int tun_init(const char *ifname, int instance_id); -/*! \fn int tun_config(char*, int, int) +/*! + * \brief This function initializes the TUN interface for MBMS + * \param[in] ifname name of the interface + */ +int tun_init_mbms(char *ifname); + +/*! * \brief This function initializes the nasmesh interface using the basic values, * basic address, network mask and broadcast address, as the default configured * ones - * \param[in] interface_id number of this interface, prepended after interface - * name + * \param[in] ifname name of the interface * \param[in] ipv4 IPv4 address of this interface as a string * \param[in] ipv6 IPv6 address of this interface as a string - * \param[in] ifprefix interface name prefix to which an interface number will - * be appended * \return true on success, otherwise false * \note * @ingroup _nas */ -bool tun_config(int interface_id, const char *ipv4, const char *ipv6, const char *ifprefix); +bool tun_config(const char* ifname, const char *ipv4, const char *ipv6); /*! * \brief Setup a IPv4 rule in table (interface_id - 1 + 10000) and route to @@ -52,12 +67,10 @@ bool tun_config(int interface_id, const char *ipv4, const char *ipv6, const char * net.ipv4.conf.all.rp_filter=2 (strict source filtering would filter out * responses of packets going out through interface to another IP address not * in same subnet). - * \param[in] interface_id number of this interface, prepended after interface - * name + * \param[in] ifname name of the interface + * \param[in] instance_id unique instance number, used to create the table * \param[in] ipv4 IPv4 address of the UE - * \param[in] ifprefix interface name prefix to which an interface number will - * be appended */ -void setup_ue_ipv4_route(int interface_id, const char *ipv4, const char *ifpref); +void setup_ue_ipv4_route(const char* ifname, int instance_id, const char *ipv4); #endif /*TUN_IF_H_*/ diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp.c b/openair2/LAYER2/PDCP_v10.1.0/pdcp.c index 3c3ef5367f..f93044f9cd 100644 --- a/openair2/LAYER2/PDCP_v10.1.0/pdcp.c +++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp.c @@ -2172,22 +2172,35 @@ uint64_t pdcp_module_init( uint64_t pdcp_optmask, int id) { if (UE_NAS_USE_TUN) { int num_if = (NFAPI_MODE == NFAPI_UE_STUB_PNF || IS_SOFTMODEM_SIML1 || NFAPI_MODE == NFAPI_MODE_STANDALONE_PNF) ? MAX_MOBILES_PER_ENB : 1; - tun_init("oaitun_ue", num_if, id); - tun_init_mbms("oaitun_uem", id + 1); - tun_config(1, "10.0.2.2", NULL, "oaitun_uem"); + int begx = (id == 0) ? 0 : id - 1; + int endx = (id == 0) ? num_if : id; + for (int i = begx; i < endx; i++) { + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, "oaitun_ue", i); + tun_init(ifname, i); + } + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, "oaitun_uem", id + 1); + tun_init_mbms(ifname); + tun_config(ifname, "10.0.2.2", NULL); LOG_I(PDCP, "UE pdcp will use tun interface\n"); } else if (ENB_NAS_USE_TUN) { - tun_init("oaitun_enb", 1, 0); - tun_config(1, "10.0.1.1", NULL, "oaitun_enb"); + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, "oaitun_enb", 0); + tun_init(ifname, 0); + tun_config(ifname, "10.0.1.1", NULL); if (pdcp_optmask & ENB_NAS_USE_TUN_W_MBMS_BIT) { - tun_init_mbms("oaitun_enm", 1); - tun_config(1, "10.0.2.1", NULL, "oaitun_enm"); + tun_generate_ifname(ifname, "oaitun_enm", 0); + tun_init_mbms(ifname); + tun_config(ifname, "10.0.2.1", NULL); LOG_I(PDCP, "ENB pdcp will use mbms tun interface\n"); } LOG_I(PDCP, "ENB pdcp will use tun interface\n"); } else if (pdcp_optmask & ENB_NAS_USE_TUN_W_MBMS_BIT) { - tun_init_mbms("oaitun_enm", 0); - tun_config(1, "10.0.2.1", NULL, "oaitun_enm"); + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, "oaitun_enm", 0); + tun_init_mbms(ifname); + tun_config(ifname, "10.0.2.1", NULL); LOG_I(PDCP, "ENB pdcp will use mbms tun interface\n"); } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index 7691871b33..54880e828a 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -640,18 +640,28 @@ uint64_t nr_pdcp_module_init(uint64_t _pdcp_optmask, int id) int num_if = (NFAPI_MODE == NFAPI_UE_STUB_PNF || IS_SOFTMODEM_SIML1 || NFAPI_MODE == NFAPI_MODE_STANDALONE_PNF) ? MAX_MOBILES_PER_ENB : 1; - tun_init(ifprefix, num_if, id); + int begx = (id == 0) ? 0 : id - 1; + int endx = (id == 0) ? num_if : id; + for (int i = begx; i < endx; i++) { + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, ifprefix, i); + tun_init(ifname, i); + } if (IS_SOFTMODEM_NOS1) { const char *ip = !get_softmodem_params()->nsa ? "10.0.1.2" : "10.0.1.3"; - tun_config(1, ip, NULL, ifprefix); + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, ifprefix, id); + tun_config(ifname, ip, NULL); set_qfi_pduid(7, 10); } LOG_I(PDCP, "UE pdcp will use tun interface\n"); start_pdcp_tun_ue(); } else if (ENB_NAS_USE_TUN) { char *ifprefix = get_softmodem_params()->nsa ? "oaitun_gnb" : "oaitun_enb"; - tun_init(ifprefix, 1, id); - tun_config(1, "10.0.1.1", NULL, ifprefix); + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, ifprefix, id); + tun_init(ifname, id); + tun_config(ifname, "10.0.1.1", NULL); LOG_I(PDCP, "ENB pdcp will use tun interface\n"); start_pdcp_tun_enb(); } diff --git a/openair2/RRC/LTE/rrc_UE.c b/openair2/RRC/LTE/rrc_UE.c index 1e12543604..f7b513d264 100644 --- a/openair2/RRC/LTE/rrc_UE.c +++ b/openair2/RRC/LTE/rrc_UE.c @@ -777,7 +777,9 @@ rrc_ue_establish_drb( "10.0.%d.%d", UE_NAS_USE_TUN ? 1 : (ip_addr_offset3 + ue_mod_idP + 1), ip_addr_offset4 + ue_mod_idP + 1); - oip_ifup = tun_config(ip_addr_offset3 + ue_mod_idP + 1, ip, NULL, "oaitun_ue"); + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, "oaitun_ue", ip_addr_offset3 + ue_mod_idP); + oip_ifup = tun_config(ifname, ip, NULL); if (oip_ifup == 0 && (!UE_NAS_USE_TUN)) { // interface is up --> send a config the DRB LOG_I(OIP,"[UE %d] Config the ue net interface %d to send/receive pkt on DRB %ld to/from the protocol stack\n", diff --git a/openair2/RRC/LTE/rrc_eNB.c b/openair2/RRC/LTE/rrc_eNB.c index 78f05cf56a..8d0fd2f8ed 100644 --- a/openair2/RRC/LTE/rrc_eNB.c +++ b/openair2/RRC/LTE/rrc_eNB.c @@ -5250,7 +5250,9 @@ rrc_eNB_process_RRCConnectionReconfigurationComplete( ctxt_pP->module_id); char ip[20]; snprintf(ip, sizeof(ip), "10.0.%d.%d", ctxt_pP->module_id + 1, ctxt_pP->module_id + 1); - oip_ifup = tun_config(ctxt_pP->module_id, ip, NULL, "oaitun_oai"); + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, "oaitun_oai", ctxt_pP->module_id); + oip_ifup = tun_config(ifname, ip, NULL); if (oip_ifup == 0) { // interface is up --> send a config the DRB module_id_t ue_module_id; diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index d7a1c42bf3..d2764ac2e1 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -855,14 +855,18 @@ static void process_pdu_session_addr(pdu_session_establishment_accept_msg_t *msg case PDU_SESSION_TYPE_IPV4: { char ip[20]; capture_ipv4_addr(&addr[0], ip, sizeof(ip)); - tun_config(1, ip, NULL, "oaitun_ue"); - setup_ue_ipv4_route(1, ip, "oaitun_ue"); + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, "oaitun_ue", 0); + tun_config(ifname, ip, NULL); + setup_ue_ipv4_route(ifname, 0, ip); } break; case PDU_SESSION_TYPE_IPV6: { char ipv6[40]; capture_ipv6_addr(addr, ipv6, sizeof(ipv6)); - tun_config(1, NULL, ipv6, "oaitun_ue"); + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, "oaitun_ue", 0); + tun_config(ifname, NULL, ipv6); } break; case PDU_SESSION_TYPE_IPV4V6: { @@ -870,8 +874,10 @@ static void process_pdu_session_addr(pdu_session_establishment_accept_msg_t *msg capture_ipv6_addr(addr, ipv6, sizeof(ipv6)); char ipv4[20]; capture_ipv4_addr(&addr[IPv6_INTERFACE_ID_LENGTH], ipv4, sizeof(ipv4)); - tun_config(1, ipv4, ipv6, "oaitun_ue"); - setup_ue_ipv4_route(1, ipv4, "oaitun_ue"); + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, "oaitun_ue", 0); + tun_config(ifname, ipv4, ipv6); + setup_ue_ipv4_route(ifname, 0, ipv4); } break; default: diff --git a/openair3/NAS/UE/ESM/esm_ebr_context.c b/openair3/NAS/UE/ESM/esm_ebr_context.c index 72b5d240e0..7a8de459e7 100644 --- a/openair3/NAS/UE/ESM/esm_ebr_context.c +++ b/openair3/NAS/UE/ESM/esm_ebr_context.c @@ -207,8 +207,10 @@ int esm_ebr_context_create( char *ip_addr = pdn->ip_addr; snprintf(ip, sizeof(ip), "%d.%d.%d.%d", ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]); const char *ifn = get_softmodem_params()->nsa ? "oaitun_nru" : "oaitun_ue"; - tun_config(1, ip, NULL, ifn); - setup_ue_ipv4_route(1, ip, ifn); + char ifname[IFNAMSIZ]; + tun_generate_ifname(ifname, ifn, 0); + tun_config(ifname, ip, NULL); + setup_ue_ipv4_route(ifname, 0, ip); } break; case NET_PDN_TYPE_IPV6: From bad4a6eb64017b03abc9c0053ab1dfbe7d1b6467 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 7 Feb 2025 14:35:28 +0100 Subject: [PATCH 20/48] nFAPI: msg length/offset is 32bit 5G nFAPI headers specify 32 bits for the length field, so use it here as well. --- nfapi/open-nFAPI/pnf/src/pnf_p7.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nfapi/open-nFAPI/pnf/src/pnf_p7.c b/nfapi/open-nFAPI/pnf/src/pnf_p7.c index 81de4d7acc..4fdb4ae01f 100644 --- a/nfapi/open-nFAPI/pnf/src/pnf_p7.c +++ b/nfapi/open-nFAPI/pnf/src/pnf_p7.c @@ -2378,7 +2378,7 @@ void pnf_nr_handle_p7_message(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7, if (rx_msg->num_segments_received == rx_msg->num_segments_expected) { // send the buffer on uint16_t i = 0; - uint16_t length = 0; + uint32_t length = 0; for (i = 0; i < rx_msg->num_segments_expected; ++i) { length += rx_msg->segments[i].length - (i > 0 ? NFAPI_NR_P7_HEADER_LENGTH : 0); } @@ -2400,7 +2400,7 @@ void pnf_nr_handle_p7_message(void* pRecvMsg, int recvMsgLen, pnf_p7_t* pnf_p7, pnf_p7->reassemby_buffer_size = length; } - uint16_t offset = 0; + uint32_t offset = 0; for (i = 0; i < rx_msg->num_segments_expected; ++i) { if (i == 0) { memcpy(pnf_p7->reassemby_buffer, rx_msg->segments[i].buffer, rx_msg->segments[i].length); From 2067831e7aa0cf7abb1771e4301fa71635f25d54 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 7 Feb 2025 16:55:12 +0100 Subject: [PATCH 21/48] nFAPI/VNF: Correctly set message length when segmenting The 5G nFAPI message length is 32bits. In particular tx_data.requests can be longer than 64kB. When segmenting, we should correctly write the message of the current segment (across all 32bits), because the length would interpreted wrongly otherwise. This fixes a bug in which tx_data.requests were discarded for 4-layer DL MIMO on 100 MHz with this message: P7 unpack message length is greater than the message buffer Further, increase the type of various (segment-related) variables to 32 bits. Currently, the maximum segment size is sxt to 65000 bytes (and in will likely remain, because the maximum UDP size is 65536); nevertheless, increase it in case we will ever go beyond this. See also commit dee68e63198f ("nFAPI: increase maximum segment size to 65000") --- nfapi/open-nFAPI/vnf/inc/vnf_p7.h | 2 +- nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h | 4 ++-- nfapi/open-nFAPI/vnf/src/vnf_p7.c | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/nfapi/open-nFAPI/vnf/inc/vnf_p7.h b/nfapi/open-nFAPI/vnf/inc/vnf_p7.h index 3684997c22..92623b41b8 100644 --- a/nfapi/open-nFAPI/vnf/inc/vnf_p7.h +++ b/nfapi/open-nFAPI/vnf/inc/vnf_p7.h @@ -27,7 +27,7 @@ typedef struct { uint8_t* buffer; - uint16_t length; + uint32_t length; } vnf_p7_rx_message_segment_t; typedef struct vnf_p7_rx_message vnf_p7_rx_message_t; diff --git a/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h b/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h index 77f0358e40..6fd7d733eb 100644 --- a/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h +++ b/nfapi/open-nFAPI/vnf/public_inc/nfapi_vnf_interface.h @@ -669,8 +669,8 @@ typedef struct nfapi_vnf_p7_config uint8_t checksum_enabled; /*! The maxium size of a P7 segement. If a message is large that this it - * will be segemented */ - uint16_t segment_size; + * will be segmented. Note: u32 to cover 4G and 5G. */ + uint32_t segment_size; uint16_t max_num_segments; /*! Configuration option for the p7 pack unpack functions*/ diff --git a/nfapi/open-nFAPI/vnf/src/vnf_p7.c b/nfapi/open-nFAPI/vnf/src/vnf_p7.c index 0caebd2130..3d2975d03c 100644 --- a/nfapi/open-nFAPI/vnf/src/vnf_p7.c +++ b/nfapi/open-nFAPI/vnf/src/vnf_p7.c @@ -501,7 +501,7 @@ int vnf_nr_p7_pack_and_send_p7_msg(vnf_p7_t* vnf_p7, nfapi_nr_p7_message_header_ // segmenting the transmit int msg_body_len = len - NFAPI_NR_P7_HEADER_LENGTH ; - int seg_body_len = vnf_p7->_public.segment_size - NFAPI_NR_P7_HEADER_LENGTH ; + uint32_t seg_body_len = vnf_p7->_public.segment_size - NFAPI_NR_P7_HEADER_LENGTH ; int segment_count = (msg_body_len / (seg_body_len)) + ((msg_body_len % seg_body_len) ? 1 : 0); int segment = 0; @@ -511,19 +511,21 @@ int vnf_nr_p7_pack_and_send_p7_msg(vnf_p7_t* vnf_p7, nfapi_nr_p7_message_header_ for(segment = 0; segment < segment_count; ++segment) { uint8_t last = 0; - uint16_t size = vnf_p7->_public.segment_size - NFAPI_NR_P7_HEADER_LENGTH; + uint32_t size = vnf_p7->_public.segment_size - NFAPI_NR_P7_HEADER_LENGTH; if(segment + 1 == segment_count) { last = 1; size = (msg_body_len) - (seg_body_len * segment); } - uint16_t segment_size = size + NFAPI_NR_P7_HEADER_LENGTH; + uint32_t segment_size = size + NFAPI_NR_P7_HEADER_LENGTH; // Update the header with the m and segement memcpy(&tx_buffer[0], buffer, NFAPI_NR_P7_HEADER_LENGTH); // set the segment length + tx_buffer[4] = (segment_size & 0xFF000000) >> 24; + tx_buffer[5] = (segment_size & 0xFF0000) >> 16; tx_buffer[6] = (segment_size & 0xFF00) >> 8; tx_buffer[7] = (segment_size & 0xFF); From 609969f46d018bc4bb7b39c038cc40162ff2bdec Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 7 Feb 2025 17:38:12 +0100 Subject: [PATCH 22/48] nFAPI/PNF: Correctly set message length when segmenting Make the same change as in parent commit, i.e., write the full 32-bit segment size. I could not test this, as we do not reach these rx_data.indication length. It's based on the fix in the parent commit, and to avoid future bad surprises. --- nfapi/open-nFAPI/pnf/inc/pnf_p7.h | 2 +- nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h | 4 ++-- nfapi/open-nFAPI/pnf/src/pnf_p7.c | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/nfapi/open-nFAPI/pnf/inc/pnf_p7.h b/nfapi/open-nFAPI/pnf/inc/pnf_p7.h index 0c14fcebef..0325c8c242 100644 --- a/nfapi/open-nFAPI/pnf/inc/pnf_p7.h +++ b/nfapi/open-nFAPI/pnf/inc/pnf_p7.h @@ -57,7 +57,7 @@ typedef struct { typedef struct { uint8_t* buffer; - uint16_t length; + uint32_t length; } pnf_p7_rx_message_segment_t; typedef struct pnf_p7_rx_message pnf_p7_rx_message_t; diff --git a/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h b/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h index 5e964f10fa..7403bd7169 100644 --- a/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h +++ b/nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h @@ -629,8 +629,8 @@ typedef struct nfapi_pnf_p7_config uint8_t checksum_enabled; /*! The maxium size of a P7 segement. If a message is large that this it - * will be segemented */ - uint16_t segment_size; + * will be segmented. Note: u32 to cover 4G and 5G */ + uint32_t segment_size; /*! The dummy subframe buffer structure that should be used in case there * are no 'valid' subframe messages */ diff --git a/nfapi/open-nFAPI/pnf/src/pnf_p7.c b/nfapi/open-nFAPI/pnf/src/pnf_p7.c index 4fdb4ae01f..bdc1634ef5 100644 --- a/nfapi/open-nFAPI/pnf/src/pnf_p7.c +++ b/nfapi/open-nFAPI/pnf/src/pnf_p7.c @@ -602,18 +602,20 @@ int pnf_nr_p7_pack_and_send_p7_message(pnf_p7_t* pnf_p7, nfapi_nr_p7_message_hea uint8_t buffer[pnf_p7->_public.segment_size]; for (segment = 0; segment < segment_count; ++segment) { uint8_t last = 0; - uint16_t size = pnf_p7->_public.segment_size - NFAPI_NR_P7_HEADER_LENGTH; + uint32_t size = pnf_p7->_public.segment_size - NFAPI_NR_P7_HEADER_LENGTH; if (segment + 1 == segment_count) { last = 1; size = (msg_body_len) - (seg_body_len * segment); } - uint16_t segment_size = size + NFAPI_NR_P7_HEADER_LENGTH; + uint32_t segment_size = size + NFAPI_NR_P7_HEADER_LENGTH; // Update the header with the m and segement memcpy(&buffer[0], tx_buf, NFAPI_NR_P7_HEADER_LENGTH); // set the segment length + buffer[4] = (segment_size & 0xFF000000) >> 24; + buffer[5] = (segment_size & 0xFF0000) >> 16; buffer[6] = (segment_size & 0xFF00) >> 8; buffer[7] = (segment_size & 0xFF); From 212fe8d2f35f94267a5c0cde3ed92bf9f326eed3 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Mon, 3 Feb 2025 15:58:04 +0100 Subject: [PATCH 23/48] Add common enc/dec E1AP libs to e1ap_lib_common.c * these functions are used in both interface management and bearer context management libraries --- openair2/E1AP/lib/CMakeLists.txt | 1 + openair2/E1AP/lib/e1ap_interface_management.c | 59 +------------- openair2/E1AP/lib/e1ap_lib_common.c | 81 +++++++++++++++++++ openair2/E1AP/lib/e1ap_lib_common.h | 5 ++ 4 files changed, 89 insertions(+), 57 deletions(-) create mode 100644 openair2/E1AP/lib/e1ap_lib_common.c diff --git a/openair2/E1AP/lib/CMakeLists.txt b/openair2/E1AP/lib/CMakeLists.txt index 27bfbcc8a2..332986dffb 100644 --- a/openair2/E1AP/lib/CMakeLists.txt +++ b/openair2/E1AP/lib/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(e1ap_lib OBJECT e1ap_bearer_context_management.c e1ap_interface_management.c + e1ap_lib_common.c ) target_link_libraries(e1ap_lib PRIVATE asn1_e1ap) diff --git a/openair2/E1AP/lib/e1ap_interface_management.c b/openair2/E1AP/lib/e1ap_interface_management.c index 8f29ba07c5..681942f58b 100644 --- a/openair2/E1AP/lib/e1ap_interface_management.c +++ b/openair2/E1AP/lib/e1ap_interface_management.c @@ -508,61 +508,6 @@ bool eq_e1ap_cuup_setup_response(const e1ap_setup_resp_t *a, const e1ap_setup_re * GNB-CU-UP E1 SETUP FAILURE * ==================================== */ -static E1AP_Cause_t encode_e1ap_cause_ie(const e1ap_cause_t *cause) -{ - E1AP_Cause_t ie; - switch (cause->type) { - case E1AP_CAUSE_RADIO_NETWORK: - ie.present = E1AP_Cause_PR_radioNetwork; - ie.choice.radioNetwork = cause->value; - break; - case E1AP_CAUSE_TRANSPORT: - ie.present = E1AP_Cause_PR_transport; - ie.choice.transport = cause->value; - break; - case E1AP_CAUSE_PROTOCOL: - ie.present = E1AP_Cause_PR_protocol; - ie.choice.protocol = cause->value; - break; - case E1AP_CAUSE_MISC: - ie.present = E1AP_Cause_PR_misc; - ie.choice.misc = cause->value; - break; - default: - ie.present = E1AP_Cause_PR_NOTHING; - break; - } - return ie; -} - -static e1ap_cause_t decode_e1ap_cause_ie(const E1AP_Cause_t *ie) -{ - e1ap_cause_t cause; - // Decode the 'choice' field based on the 'present' value - switch (ie->present) { - case E1AP_Cause_PR_radioNetwork: - cause.value = ie->choice.radioNetwork; - cause.type = E1AP_CAUSE_RADIO_NETWORK; - break; - case E1AP_Cause_PR_transport: - cause.value = ie->choice.transport; - cause.type = E1AP_CAUSE_TRANSPORT; - break; - case E1AP_Cause_PR_protocol: - cause.value = ie->choice.protocol; - cause.type = E1AP_CAUSE_PROTOCOL; - break; - case E1AP_Cause_PR_misc: - cause.value = ie->choice.misc; - cause.type = E1AP_CAUSE_MISC; - break; - default: - cause.type = E1AP_CAUSE_NOTHING; - break; - } - return cause; -} - /** * @brief Encoder for GNB-CU-UP E1 Setup Failure * @ref 9.2.1.6 GNB-CU-UP E1 SETUP FAILURE of 3GPP TS 38.463 @@ -588,7 +533,7 @@ E1AP_E1AP_PDU_t *encode_e1ap_cuup_setup_failure(const e1ap_setup_fail_t *msg) ie2->id = E1AP_ProtocolIE_ID_id_Cause; ie2->criticality = E1AP_Criticality_ignore; ie2->value.present = E1AP_GNB_CU_UP_E1SetupFailureIEs__value_PR_Cause; - ie2->value.choice.Cause = encode_e1ap_cause_ie(&msg->cause); + ie2->value.choice.Cause = e1_encode_cause_ie(&msg->cause); // Time To Wait (O) if (msg->time_to_wait) { asn1cSequenceAdd(out->protocolIEs.list, E1AP_GNB_CU_UP_E1SetupFailureIEs_t, ie3); @@ -630,7 +575,7 @@ bool decode_e1ap_cuup_setup_failure(const E1AP_E1AP_PDU_t *pdu, e1ap_setup_fail_ case E1AP_ProtocolIE_ID_id_Cause: // Cause E1AP_LIB_FIND_IE(E1AP_GNB_CU_UP_E1SetupFailureIEs_t, ie, in, E1AP_ProtocolIE_ID_id_Cause, true); - out->cause = decode_e1ap_cause_ie(&ie->value.choice.Cause); + out->cause = e1_decode_cause_ie(&ie->value.choice.Cause); break; case E1AP_ProtocolIE_ID_id_Transport_Layer_Address_Info: // Time To Wait (O) diff --git a/openair2/E1AP/lib/e1ap_lib_common.c b/openair2/E1AP/lib/e1ap_lib_common.c new file mode 100644 index 0000000000..02923b7cf7 --- /dev/null +++ b/openair2/E1AP/lib/e1ap_lib_common.c @@ -0,0 +1,81 @@ +/* + * 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 "e1ap_lib_common.h" +#include "e1ap_lib_includes.h" +#include "e1ap_messages_types.h" + +E1AP_Cause_t e1_encode_cause_ie(const e1ap_cause_t *cause) +{ + E1AP_Cause_t ie; + switch (cause->type) { + case E1AP_CAUSE_RADIO_NETWORK: + ie.present = E1AP_Cause_PR_radioNetwork; + ie.choice.radioNetwork = cause->value; + break; + case E1AP_CAUSE_TRANSPORT: + ie.present = E1AP_Cause_PR_transport; + ie.choice.transport = cause->value; + break; + case E1AP_CAUSE_PROTOCOL: + ie.present = E1AP_Cause_PR_protocol; + ie.choice.protocol = cause->value; + break; + case E1AP_CAUSE_MISC: + ie.present = E1AP_Cause_PR_misc; + ie.choice.misc = cause->value; + break; + default: + ie.present = E1AP_Cause_PR_NOTHING; + PRINT_ERROR("encode_e1ap_cause_ie: no cause value provided\n"); + break; + } + return ie; +} + +e1ap_cause_t e1_decode_cause_ie(const E1AP_Cause_t *ie) +{ + e1ap_cause_t cause; + // Decode the 'choice' field based on the 'present' value + switch (ie->present) { + case E1AP_Cause_PR_radioNetwork: + cause.value = ie->choice.radioNetwork; + cause.type = E1AP_CAUSE_RADIO_NETWORK; + break; + case E1AP_Cause_PR_transport: + cause.value = ie->choice.transport; + cause.type = E1AP_CAUSE_TRANSPORT; + break; + case E1AP_Cause_PR_protocol: + cause.value = ie->choice.protocol; + cause.type = E1AP_CAUSE_PROTOCOL; + break; + case E1AP_Cause_PR_misc: + cause.value = ie->choice.misc; + cause.type = E1AP_CAUSE_MISC; + break; + default: + cause.type = E1AP_CAUSE_NOTHING; + PRINT_ERROR("decode_e1ap_cause_ie: no cause value provided\n"); + break; + } + return cause; +} diff --git a/openair2/E1AP/lib/e1ap_lib_common.h b/openair2/E1AP/lib/e1ap_lib_common.h index eceddb263f..be11100d90 100644 --- a/openair2/E1AP/lib/e1ap_lib_common.h +++ b/openair2/E1AP/lib/e1ap_lib_common.h @@ -26,6 +26,7 @@ #include #include #include "openair3/UTILS/conversions.h" +#include "e1ap_messages_types.h" #ifdef ENABLE_TESTS #define PRINT_ERROR(...) fprintf(stderr, ##__VA_ARGS__); @@ -98,4 +99,8 @@ } \ } while (0) +struct E1AP_Cause; +struct E1AP_Cause e1_encode_cause_ie(const e1ap_cause_t *cause); +e1ap_cause_t e1_decode_cause_ie(const struct E1AP_Cause *ie); + #endif /* E1AP_LIB_COMMON_H_ */ From 32310cced30453a3f26a0b2b90e5c21e745a4d35 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 6 Feb 2025 18:28:01 +0100 Subject: [PATCH 24/48] Bugfix: free E1 CU-UP Setup Req on CU-UP disconnection --- openair2/RRC/NR/rrc_gNB_cuup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/openair2/RRC/NR/rrc_gNB_cuup.c b/openair2/RRC/NR/rrc_gNB_cuup.c index b2ef792ac5..689670fa85 100644 --- a/openair2/RRC/NR/rrc_gNB_cuup.c +++ b/openair2/RRC/NR/rrc_gNB_cuup.c @@ -241,6 +241,7 @@ void rrc_gNB_process_e1_lost_connection(gNB_RRC_INST *rrc, e1ap_lost_connection_ if (cuup->setup_req != NULL) { e1ap_setup_req_t *req = cuup->setup_req; LOG_I(NR_RRC, "releasing CU-UP %s on assoc_id %d\n", req->gNB_cu_up_name, assoc_id); + free_e1ap_cuup_setup_request(cuup->setup_req); free(cuup->setup_req); } nr_rrc_cuup_container_t *removed = RB_REMOVE(rrc_cuup_tree, &rrc->cuups, cuup); From b9acf4a8e32a3f1a036f90e2a5f5a2a943e747ae Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Feb 2025 16:24:50 +0100 Subject: [PATCH 25/48] Refactor E1 UE removal in separate function Refactor into remove_ue_e1(), to be used in the next commit, independent of e1_bearer_release_cmd(). --- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index 380ae1f60a..dc9b5b52ee 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -300,24 +300,28 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) get_e1_if()->bearer_modif_response(&modif); } -void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd) +static void remove_ue_e1(const uint32_t ue_id) { bool need_ue_id_mgmt = e1_used(); instance_t n3inst = get_n3_gtp_instance(); instance_t f1inst = get_f1_gtp_instance(); - LOG_I(E1AP, "releasing UE %d\n", cmd->gNB_cu_up_ue_id); - - newGtpuDeleteAllTunnels(n3inst, cmd->gNB_cu_up_ue_id); + newGtpuDeleteAllTunnels(n3inst, ue_id); if (f1inst >= 0) // is there F1-U? - newGtpuDeleteAllTunnels(f1inst, cmd->gNB_cu_up_ue_id); + newGtpuDeleteAllTunnels(f1inst, ue_id); if (need_ue_id_mgmt) { // see issue #706: in monolithic, gNB will free PDCP of UE - nr_pdcp_remove_UE(cmd->gNB_cu_up_ue_id); - cu_remove_f1_ue_data(cmd->gNB_cu_up_ue_id); + nr_pdcp_remove_UE(ue_id); + cu_remove_f1_ue_data(ue_id); } - nr_sdap_delete_ue_entities(cmd->gNB_cu_up_ue_id); + nr_sdap_delete_ue_entities(ue_id); +} + +void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd) +{ + LOG_I(E1AP, "releasing UE %d\n", cmd->gNB_cu_up_ue_id); + remove_ue_e1(cmd->gNB_cu_up_ue_id); e1ap_bearer_release_cplt_t cplt = { .gNB_cu_cp_ue_id = cmd->gNB_cu_cp_ue_id, From deba3ae066df7076f8918b491faed84bedaa4e23 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 5 Feb 2025 16:44:31 +0100 Subject: [PATCH 26/48] Reset CU-UP E1 UE contexts after E1 Setup Response 38.463 sec. 8.2.3 says > This procedure also re-initialises the E1AP UE-related contexts (if > any) and erases all related signalling connections in the two nodes like > a Reset procedure would do. Hence, delete all contexts after reception of E1 Setup Response. This minimizes a risk of receiving an E1 bearer setup req for an existing UE, which currently leads to an assert [we trust the CU-CP does not send the same UE ID twice, as we only "mirror" the existing UE ID]. --- openair2/E1AP/e1ap.c | 2 ++ openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 12 ++++++++++++ openair2/LAYER2/nr_pdcp/cucp_cuup_handler.h | 1 + 3 files changed, 15 insertions(+) diff --git a/openair2/E1AP/e1ap.c b/openair2/E1AP/e1ap.c index 95046cb652..e085a9e9b9 100644 --- a/openair2/E1AP/e1ap.c +++ b/openair2/E1AP/e1ap.c @@ -219,6 +219,8 @@ int e1apCUUP_handle_SETUP_RESPONSE(sctp_assoc_t assoc_id, e1ap_upcp_inst_t *inst return -1; } free_e1ap_cuup_setup_response(&out); + + e1_reset(); // reset all UE contexts, if any: see 38.463 sec 8.2.3 return 0; } diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index dc9b5b52ee..dd281fb77d 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -330,3 +330,15 @@ void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd) get_e1_if()->bearer_release_complete(&cplt); } + +void e1_reset(void) +{ + /* we get the list of all UEs from the PDCP, which maintains a list */ + ue_id_t ue_ids[MAX_MOBILES_PER_GNB]; + int num = nr_pdcp_get_num_ues(ue_ids, MAX_MOBILES_PER_GNB); + for (uint32_t i = 0; i < num; ++i) { + ue_id_t ue_id = ue_ids[i]; + LOG_W(E1AP, "releasing UE %ld\n", ue_id); + remove_ue_e1(ue_id); + } +} diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.h b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.h index 7290043371..521a33ea99 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.h +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.h @@ -31,5 +31,6 @@ struct e1ap_bearer_release_cmd_s; void e1_bearer_context_setup(const struct e1ap_bearer_setup_req_s *req); void e1_bearer_context_modif(const struct e1ap_bearer_setup_req_s *req); void e1_bearer_release_cmd(const struct e1ap_bearer_release_cmd_s *cmd); +void e1_reset(void); #endif /* CUCP_CUUP_HANDLER_H */ From 27a418572d3f8f73bda5ed78f9900d5b1b756a31 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Feb 2025 08:59:47 +0100 Subject: [PATCH 27/48] Remove unnecessary includes f1ap_lib_common should help with common message enc/dec inside lib/, and not be used directly. --- openair2/F1AP/f1ap_cu_interface_management.c | 1 - openair2/F1AP/f1ap_du_interface_management.c | 1 - 2 files changed, 2 deletions(-) diff --git a/openair2/F1AP/f1ap_cu_interface_management.c b/openair2/F1AP/f1ap_cu_interface_management.c index 9e25aeb63e..b81d81c6ba 100644 --- a/openair2/F1AP/f1ap_cu_interface_management.c +++ b/openair2/F1AP/f1ap_cu_interface_management.c @@ -36,7 +36,6 @@ #include "f1ap_cu_interface_management.h" #include "f1ap_default_values.h" #include "lib/f1ap_interface_management.h" -#include "lib/f1ap_lib_common.h" int CU_handle_RESET_ACKNOWLEDGE(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu) { diff --git a/openair2/F1AP/f1ap_du_interface_management.c b/openair2/F1AP/f1ap_du_interface_management.c index f12b104263..0d0c9c2946 100644 --- a/openair2/F1AP/f1ap_du_interface_management.c +++ b/openair2/F1AP/f1ap_du_interface_management.c @@ -35,7 +35,6 @@ #include "f1ap_itti_messaging.h" #include "f1ap_du_interface_management.h" #include "lib/f1ap_interface_management.h" -#include "lib/f1ap_lib_common.h" #include "openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.h" #include "assertions.h" From f267641bf9be3ffee25ef75c8539b066612482a1 Mon Sep 17 00:00:00 2001 From: Raghavendra Dinavahi Date: Fri, 7 Feb 2025 18:20:03 +0100 Subject: [PATCH 28/48] Changes in function prepare_sim_uecap --- .../LAYER2/NR_MAC_UE/nr_ue_power_procedures.c | 2 +- .../tests/test_nr_ue_power_procedures.cpp | 2 +- openair2/RRC/NR/nr_rrc_config.c | 67 +++++++++++++++++-- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c index 70d3aacb66..b277352105 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c @@ -233,7 +233,7 @@ float nr_get_Pcmax(int p_Max, float nr_get_Pcmin(int bandwidth_index) { const float table_38101_6_3_1_1[] = { - -40, -40, -40, -40, -39, -38.2, -37.5, -37, -36.5, -35.2, -34.6, -34, -33.5, -33 + -40, -40, -40, -40, -39, -38.2, -37.5, -37, -36.5, -36, -35.2, -34.6, -34, -33.5, -33 }; return table_38101_6_3_1_1[bandwidth_index]; } diff --git a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp index ac7ed2f73a..59f932ac57 100644 --- a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp +++ b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp @@ -178,7 +178,7 @@ TEST(test_pucch_power_state, test_accumulated_delta_pucch) TEST(pc_min, check_all_bw_indexes) { - const int bws[] = {5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 90, 100}; + const int bws[] = {5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100}; for (auto i = 0U; i < sizeofArray(bws); i++) { (void)nr_get_Pcmin(i); } diff --git a/openair2/RRC/NR/nr_rrc_config.c b/openair2/RRC/NR/nr_rrc_config.c index 18487e9657..1a38014564 100644 --- a/openair2/RRC/NR/nr_rrc_config.c +++ b/openair2/RRC/NR/nr_rrc_config.c @@ -822,6 +822,48 @@ static NR_SetupRelease_SRS_Config_t *get_config_srs(const NR_ServingCellConfigCo return setup_release_srs_Config; } +static int get_SupportedBandwidth_fr1_bw_index(int bw_index) +{ + int val = -1; + + switch (bw_index) { + case NR_SupportedBandwidth_v1700__fr1_r17_mhz5: + case NR_SupportedBandwidth_v1700__fr1_r17_mhz10: + case NR_SupportedBandwidth_v1700__fr1_r17_mhz15: + case NR_SupportedBandwidth_v1700__fr1_r17_mhz20: + case NR_SupportedBandwidth_v1700__fr1_r17_mhz25: + val = bw_index; + break; + case NR_SupportedBandwidth_v1700__fr1_r17_mhz30: + case NR_SupportedBandwidth_v1700__fr1_r17_mhz35: + val = NR_SupportedBandwidth__fr1_mhz30; + break; + case NR_SupportedBandwidth_v1700__fr1_r17_mhz40: + case NR_SupportedBandwidth_v1700__fr1_r17_mhz45: + val = NR_SupportedBandwidth__fr1_mhz40; + break; + case NR_SupportedBandwidth_v1700__fr1_r17_mhz50: + val = NR_SupportedBandwidth__fr1_mhz50; + break; + case NR_SupportedBandwidth_v1700__fr1_r17_mhz60: + val = NR_SupportedBandwidth__fr1_mhz60; + break; + case NR_SupportedBandwidth_v1700__fr1_r17_mhz70: + case NR_SupportedBandwidth_v1700__fr1_r17_mhz80: + val = NR_SupportedBandwidth__fr1_mhz80; + break; + case NR_SupportedBandwidth_v1700__fr1_r17_mhz90: + case NR_SupportedBandwidth_v1700__fr1_r17_mhz100: + val = NR_SupportedBandwidth__fr1_mhz100; + break; + default: + AssertFatal(1==0, "Invalid bw index\n"); + } + + return val; +} + + void prepare_sim_uecap(NR_UE_NR_Capability_t *cap, NR_ServingCellConfigCommon_t *scc, int numerology, @@ -858,26 +900,39 @@ void prepare_sim_uecap(NR_UE_NR_Capability_t *cap, fs->featureSetsDownlinkPerCC = calloc(1, sizeof(*fs->featureSetsDownlinkPerCC)); NR_FeatureSetDownlinkPerCC_t *fs_cc = calloc(1, sizeof(*fs_cc)); fs_cc->supportedSubcarrierSpacingDL = numerology; - int bw = get_supported_band_index(numerology, freq_range, rbsize); - if (bw == 10) // 90MHz + int bw_index = get_supported_band_index(numerology, freq_range, rbsize); + int bw = get_supported_bw_mhz(freq_range, bw_index); + if (bw == 90) // 90MHz fs_cc->channelBW_90mhz = calloc(1, sizeof(*fs_cc->channelBW_90mhz)); - if (bw == 11) // 100MHz - bw--; if(freq_range == FR2) { fs_cc->supportedBandwidthDL.present = NR_SupportedBandwidth_PR_fr2; - fs_cc->supportedBandwidthDL.choice.fr2 = bw; + fs_cc->supportedBandwidthDL.choice.fr2 = bw_index; } else{ fs_cc->supportedBandwidthDL.present = NR_SupportedBandwidth_PR_fr1; - fs_cc->supportedBandwidthDL.choice.fr1 = bw; + fs_cc->supportedBandwidthDL.choice.fr1 = get_SupportedBandwidth_fr1_bw_index(bw_index); } fs_cc->supportedModulationOrderDL = calloc(1, sizeof(*fs_cc->supportedModulationOrderDL)); *fs_cc->supportedModulationOrderDL = NR_ModulationOrder_qam256; asn1cSeqAdd(&fs->featureSetsDownlinkPerCC->list, fs_cc); + + if (bw == 35 || bw == 45 || bw == 70) { + fs->ext6 = calloc(1, sizeof(*fs->ext6)); + fs->ext6->featureSetsDownlinkPerCC_v1700 = calloc(1, sizeof(*fs->ext6->featureSetsDownlinkPerCC_v1700)); + NR_FeatureSetDownlinkPerCC_v1700_t *fs_dlcc_v1700 = calloc(1, sizeof(*fs_dlcc_v1700)); + fs_dlcc_v1700->supportedBandwidthDL_v1710 = calloc(1, sizeof(*fs_dlcc_v1700->supportedBandwidthDL_v1710)); + fs_dlcc_v1700->supportedBandwidthDL_v1710->present = NR_SupportedBandwidth_v1700_PR_fr1_r17; + fs_dlcc_v1700->supportedBandwidthDL_v1710->choice.fr1_r17 = bw_index; + asn1cSeqAdd(&fs->ext6->featureSetsDownlinkPerCC_v1700->list, fs_dlcc_v1700); + } } phy_Parameters->phy_ParametersFRX_Diff = calloc(1, sizeof(*phy_Parameters->phy_ParametersFRX_Diff)); phy_Parameters->phy_ParametersFRX_Diff->pucch_F0_2WithoutFH = NULL; + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { + xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, cap); + } } void nr_rrc_config_dl_tda(struct NR_PDSCH_TimeDomainResourceAllocationList *pdsch_TimeDomainAllocationList, From b862963a95060c0b9d7277b7b2dc1386a8dbd6cf Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 4 Feb 2025 11:14:59 +0100 Subject: [PATCH 29/48] Add macro to check result of dec lib functions and return with a failure --- openair2/E1AP/lib/e1ap_lib_common.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openair2/E1AP/lib/e1ap_lib_common.h b/openair2/E1AP/lib/e1ap_lib_common.h index be11100d90..39e3d64448 100644 --- a/openair2/E1AP/lib/e1ap_lib_common.h +++ b/openair2/E1AP/lib/e1ap_lib_common.h @@ -34,6 +34,14 @@ #define PRINT_ERROR(...) // Do nothing #endif +#define CHECK_E1AP_DEC(exp) \ + do { \ + if (!(exp)) { \ + PRINT_ERROR("Failed executing " #exp " in %s() line %d\n", __func__, __LINE__); \ + return false; \ + } \ + } while (0) + #define _E1_EQ_CHECK_GENERIC(condition, fmt, ...) \ do { \ if (!(condition)) { \ From 4f613ab2af372f48126be268e2f8001d3dac1c3e Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 4 Feb 2025 11:31:33 +0100 Subject: [PATCH 30/48] Remove incomplete activityNotificationLevel handling --- openair2/COMMON/e1ap_messages_types.h | 7 ------- openair2/E1AP/lib/e1ap_bearer_context_management.c | 7 ------- openair2/E1AP/tests/e1ap_lib_test.c | 1 - 3 files changed, 15 deletions(-) diff --git a/openair2/COMMON/e1ap_messages_types.h b/openair2/COMMON/e1ap_messages_types.h index 7fed096971..458cfc1f46 100644 --- a/openair2/COMMON/e1ap_messages_types.h +++ b/openair2/COMMON/e1ap_messages_types.h @@ -138,12 +138,6 @@ typedef enum BEARER_CONTEXT_STATUS_e { BEARER_RESUME, } BEARER_CONTEXT_STATUS_t; -typedef enum activity_notification_level_e { - ANL_DRB = 0, - ANL_PDU_SESSION, - ANL_UE, -} activity_notification_level_t; - typedef enum cell_group_id_e { MCG = 0, SCG, @@ -413,7 +407,6 @@ typedef struct e1ap_bearer_setup_req_s { long ueDlAggMaxBitRate; PLMN_ID_t servingPLMNid; BEARER_CONTEXT_STATUS_t bearerContextStatus; - activity_notification_level_t activityNotificationLevel; int numPDUSessions; pdu_session_to_setup_t pduSession[E1AP_MAX_NUM_PDU_SESSIONS]; int numPDUSessionsMod; diff --git a/openair2/E1AP/lib/e1ap_bearer_context_management.c b/openair2/E1AP/lib/e1ap_bearer_context_management.c index a67d4b5b08..2552e478ba 100644 --- a/openair2/E1AP/lib/e1ap_bearer_context_management.c +++ b/openair2/E1AP/lib/e1ap_bearer_context_management.c @@ -544,12 +544,6 @@ bool decode_E1_bearer_context_setup_request(const E1AP_E1AP_PDU_t *pdu, e1ap_bea out->servingPLMNid.mnc_digit_length); break; - case E1AP_ProtocolIE_ID_id_ActivityNotificationLevel: - _E1_EQ_CHECK_INT(ie->value.present, E1AP_BearerContextSetupRequestIEs__value_PR_ActivityNotificationLevel); - DevAssert(ie->value.present == E1AP_BearerContextSetupRequestIEs__value_PR_ActivityNotificationLevel); - out->activityNotificationLevel = ie->value.choice.ActivityNotificationLevel; - break; - case E1AP_ProtocolIE_ID_id_System_BearerContextSetupRequest: _E1_EQ_CHECK_INT(ie->value.present, E1AP_BearerContextSetupRequestIEs__value_PR_System_BearerContextSetupRequest); E1AP_System_BearerContextSetupRequest_t *System_BearerContextSetupRequest = @@ -629,7 +623,6 @@ bool eq_bearer_context_setup_request(const e1ap_bearer_setup_req_t *a, const e1a _E1_EQ_CHECK_LONG(a->cipheringAlgorithm, b->cipheringAlgorithm); _E1_EQ_CHECK_LONG(a->integrityProtectionAlgorithm, b->integrityProtectionAlgorithm); _E1_EQ_CHECK_LONG(a->ueDlAggMaxBitRate, b->ueDlAggMaxBitRate); - _E1_EQ_CHECK_INT(a->activityNotificationLevel, b->activityNotificationLevel); _E1_EQ_CHECK_INT(a->numPDUSessions, b->numPDUSessions); _E1_EQ_CHECK_INT(a->numPDUSessionsMod, b->numPDUSessionsMod); // PLMN diff --git a/openair2/E1AP/tests/e1ap_lib_test.c b/openair2/E1AP/tests/e1ap_lib_test.c index 91eab080a9..9a53e8074d 100644 --- a/openair2/E1AP/tests/e1ap_lib_test.c +++ b/openair2/E1AP/tests/e1ap_lib_test.c @@ -114,7 +114,6 @@ static void test_bearer_context_setup_request(void) .servingPLMNid.mcc = 001, .servingPLMNid.mnc = 01, .servingPLMNid.mnc_digit_length = 0x02, - .activityNotificationLevel = ANL_PDU_SESSION, .numPDUSessions = 1, .pduSession[0].sessionId = 1, .pduSession[0].sessionType = E1AP_PDU_Session_Type_ipv4, From 2146f29c44e77f81615c7c089c65d6f32e32a784 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 4 Feb 2025 11:56:40 +0100 Subject: [PATCH 31/48] Refactor E1 Bearer Context Management structs Bearer context setup request and modification request messages differ in various IEs according to 9.2.2 of 3GPP TS 38.463, e.g. the Bearer context setup request does not contain a list of DRBs to modify and does not contain an UP TL configuration. Thus, the goal of this commit is to split the setup request message and the modification request into two different structs: * adopt e1ap_bearer_mod_req_t where necessary (e.g. fill_e1_bearer_modif) Also, reorganized existing struct members according to specs: * remove irrelevant struct members from the setup request * group members from the same IEs into structs * set optional struct members IEs as pointers * add and adopt enc/dec/cp/eq lib functions for single IEs * add UP_TL_information_t struct with TEID and TL address info and adopt in stack * use int instead of long for count types * update the relevant copy/equality check utility functions Extra: * add a function to handle Security Information in RRC in the process * add create_up_tl_info and adopt in unit tests --- openair1/SIMULATION/NR_PHY/dlsim.c | 2 +- openair1/SIMULATION/NR_PHY/psbchsim.c | 2 +- openair1/SIMULATION/NR_PHY/ulsim.c | 2 +- openair2/COMMON/e1ap_messages_types.h | 239 +++++-- openair2/E1AP/e1ap.c | 20 +- .../E1AP/lib/e1ap_bearer_context_management.c | 590 ++++++++++-------- openair2/E1AP/lib/e1ap_lib_includes.h | 1 + openair2/E1AP/tests/e1ap_lib_test.c | 25 +- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 35 +- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.h | 3 +- openair2/RRC/NR/cucp_cuup_if.h | 2 + openair2/RRC/NR/nr_rrc_defs.h | 2 +- openair2/RRC/NR/rrc_gNB.c | 54 +- openair2/RRC/NR/rrc_gNB_NGAP.c | 9 +- 14 files changed, 585 insertions(+), 401 deletions(-) diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index 236913d3f8..67da0bb428 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -125,7 +125,7 @@ void nr_derive_key_ng_ran_star(uint16_t pci, uint64_t nr_arfcn_dl, const uint8_t int dummy_nr_ue_ul_indication(nr_uplink_indication_t *ul_info) { return(0); } void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) { abort(); } -void e1_bearer_context_modif(const e1ap_bearer_setup_req_t *req) { abort(); } +void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) { abort(); } void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd) { abort(); } int8_t nr_rrc_RA_succeeded(const module_id_t mod_id, const uint8_t gNB_index) { diff --git a/openair1/SIMULATION/NR_PHY/psbchsim.c b/openair1/SIMULATION/NR_PHY/psbchsim.c index 972605fc96..f5b0f88ab2 100644 --- a/openair1/SIMULATION/NR_PHY/psbchsim.c +++ b/openair1/SIMULATION/NR_PHY/psbchsim.c @@ -51,7 +51,7 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) { abort(); } -void e1_bearer_context_modif(const e1ap_bearer_setup_req_t *req) +void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) { abort(); } diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 301beb19f5..5f2b7ce6c9 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -126,7 +126,7 @@ void nr_derive_key_ng_ran_star(uint16_t pci, uint64_t nr_arfcn_dl, const uint8_t extern void fix_scd(NR_ServingCellConfig_t *scd);// forward declaration void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) { abort(); } -void e1_bearer_context_modif(const e1ap_bearer_setup_req_t *req) { abort(); } +void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) { abort(); } void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd) { abort(); } int8_t nr_rrc_RA_succeeded(const module_id_t mod_id, const uint8_t gNB_index) { diff --git a/openair2/COMMON/e1ap_messages_types.h b/openair2/COMMON/e1ap_messages_types.h index 458cfc1f46..2f59cb62d6 100644 --- a/openair2/COMMON/e1ap_messages_types.h +++ b/openair2/COMMON/e1ap_messages_types.h @@ -252,10 +252,16 @@ typedef struct e1ap_setup_fail_s { criticality_diagnostics_t *crit_diag; } e1ap_setup_fail_t; -typedef struct up_params_s { +typedef struct UP_TL_information_s { in_addr_t tlAddress; - long teId; - int cell_group_id; + int32_t teId; +} UP_TL_information_t; + +typedef struct up_params_s { + // Transport layer info + UP_TL_information_t tl_info; + // Cell Group ID (M) + cell_group_id_t cell_group_id; } up_params_t; /* IE SDAP Configuration (clause 9.3.1.39 of 3GPP TS 38.463) */ @@ -325,38 +331,63 @@ typedef struct qos_flow_level_qos_parameters_s { ngran_allocation_retention_priority_t alloc_reten_priority; // additional members should be added!! } qos_flow_level_qos_parameters_t; +// QoS Flow QoS Parameters List 9.3.1.25 typedef struct qos_flow_setup_e { long qfi; // qos flow identifier qos_flow_level_qos_parameters_t qos_params; } qos_flow_to_setup_t; -/* DRB To Setup List according to 3GPP TS 38.463 */ +// 9.3.1.60 QoS Flow Mapping Indication +typedef enum qos_flow_mapping_indication_e { + QF_UL = 0, + QF_DL, + QF_BOTH +} qos_flow_mapping_indication_t; + +typedef struct qos_flow_list_s { + long qfi; // qos flow identifier + qos_flow_mapping_indication_t *indication; +} qos_flow_list_t; + +/** + * DRB To Setup List according to 3GPP TS 38.463 + */ typedef struct DRB_nGRAN_to_setup_s { - /* DRB ID (clause 9.3.1.16) */ + // DRB ID (M) (clause 9.3.1.16) long id; - - /* SDAP Configuration (clause 9.3.1.39) */ + // SDAP Configuration (M) (clause 9.3.1.39) bearer_context_sdap_config_t sdap_config; - /* PDCP Configuration (clause 9.3.1.38) */ + // PDCP Configuration (M) (clause 9.3.1.38) bearer_context_pdcp_config_t pdcp_config; - - /* DRB Data Forwarding Information (clause 9.3.2.6) */ - /* Transport Layer Address (clause 9.3.2.4) */ - in_addr_t tlAddress; - /* GTP-TEID (clause 9.3.2.3) */ - int teId; - /* DL UP Transport Layer Information (clause 9.3.2.1) */ - int numDlUpParam; - up_params_t DlUpParamList[E1AP_MAX_NUM_UP_PARAM]; - - /* Cell Group Information (clause 9.3.1.11) */ + // DRB Inactivity Timer (O) + int *drb_inactivity_timer; + // Cell Group Information (M) (clause 9.3.1.11) int numCellGroups; cell_group_id_t cellGroupList[E1AP_MAX_NUM_CELL_GROUPS]; - - /* DRB QoS Flows Parameters (clause 9.3.1.26) */ + // QoS Flows Information To Be Setup (M) (clause 9.3.1.25, 9.3.1.26) int numQosFlow2Setup; qos_flow_to_setup_t qosFlows[E1AP_MAX_NUM_QOS_FLOWS]; -} DRB_nGRAN_to_setup_t, DRB_nGRAN_to_mod_t; +} DRB_nGRAN_to_setup_t; + +/** + * DRB To Modify List according to 3GPP TS 38.463 + */ +typedef struct DRB_nGRAN_to_modify_s { + // DRB ID (M) (clause 9.3.1.16) + long id; + // SDAP Configuration (O) (clause 9.3.1.39) + bearer_context_sdap_config_t *sdap_config; + // PDCP Configuration (O) (clause 9.3.1.38) + bearer_context_pdcp_config_t *pdcp_config; + // PDCP SN Status Request (O) + bool pdcp_sn_status_requested; + // DL UP Transport Layer Information (O) (clause 9.3.1.13, 9.3.2.1) + int numDlUpParam; + up_params_t DlUpParamList[E1AP_MAX_NUM_UP_PARAM]; + // QoS Flows Information To Modify (O) (clause 9.3.1.25, 9.3.1.26) + int numQosFlow2Setup; + qos_flow_to_setup_t qosFlows[E1AP_MAX_NUM_QOS_FLOWS]; +} DRB_nGRAN_to_mod_t; typedef enum e1ap_indication_e { SECURITY_REQUIRED = 0, @@ -371,47 +402,108 @@ typedef struct security_indication_s { long maxIPrate; } security_indication_t; -typedef struct UP_TL_information_s { - in_addr_t tlAddress; - int32_t teId; -} UP_TL_information_t; - -/** - * PDU Session Resource To Setup List (clause 9.3.3.10) - * PDU Session Resource To Modify List (clause 9.3.3.11) -*/ -typedef struct pdu_session_to_setup_s { - long sessionId; - long sessionType; - e1ap_nssai_t nssai; - security_indication_t securityIndication; - UP_TL_information_t UP_TL_information; - long numDRB2Setup; - DRB_nGRAN_to_setup_t DRBnGRanList[E1AP_MAX_NUM_DRBS]; - long numDRB2Modify; - DRB_nGRAN_to_mod_t DRBnGRanModList[E1AP_MAX_NUM_DRBS]; -} pdu_session_to_setup_t, pdu_session_to_mod_t; - -/** - * Bearer Context Setup Request message, clause 9.2.2.1 of 3GPP TS 38.463 - * out of simplicity, this same struct is used for clause 9.2.2.4 - * i.e. Bearer Context Modification Request message - */ -typedef struct e1ap_bearer_setup_req_s { - uint32_t gNB_cu_cp_ue_id; - uint32_t gNB_cu_up_ue_id; // Bearer Context Modification Request only +// Security Information (9.3.1.10) +typedef struct security_information_s { uint64_t cipheringAlgorithm; uint64_t integrityProtectionAlgorithm; char encryptionKey[E1AP_SECURITY_KEY_SIZE]; char integrityProtectionKey[E1AP_SECURITY_KEY_SIZE]; - long ueDlAggMaxBitRate; +} security_information_t; + +/** + * PDU Session Resource To Setup List (clause 9.3.3.10) + */ +typedef struct pdu_session_to_setup_s { + // PDU Session ID (M) + long sessionId; + // PDU Session Type (M) + long sessionType; + // S-NSSAI (M) + e1ap_nssai_t nssai; + // Security Indication (M) + security_indication_t securityIndication; + // PDU Session Resource DL Aggregate Maximum Bit Rate (O) + long *dlAggregateMaxBitRate; + // NG UL UP Transport Layer Information (M) + UP_TL_information_t UP_TL_information; + // PDU Session Inactivity Timer (O) + int *inactivityTimer; + // DRB To Setup List (M) + int numDRB2Setup; + // DRB To Setup Item (1..) + DRB_nGRAN_to_setup_t DRBnGRanList[E1AP_MAX_NUM_DRBS]; +} pdu_session_to_setup_t; + +/** + * PDU Session Resource To Modify List (clause 9.3.3.11) + */ +typedef struct pdu_session_to_mod_s { + // PDU Session ID (M) + long sessionId; + // Security Indication (O) + security_indication_t *securityIndication; + // NG UL UP Transport Layer Information (O) + UP_TL_information_t *UP_TL_information; + // DRB To Setup List (0..1) + int numDRB2Setup; + // DRB To Setup Item (1..) + DRB_nGRAN_to_setup_t DRBnGRanList[E1AP_MAX_NUM_DRBS]; + // DRB To Modify List (0..1) + long numDRB2Modify; + // DRB To Modify Item (1..) + DRB_nGRAN_to_mod_t DRBnGRanModList[E1AP_MAX_NUM_DRBS]; +} pdu_session_to_mod_t; + +/** + * Bearer Context Setup Request message, clause 9.2.2.1 of 3GPP TS 38.463 + */ +typedef struct e1ap_bearer_setup_req_s { + // gNB-CU-CP UE E1AP ID (M) + uint32_t gNB_cu_cp_ue_id; + // Security Information (M) + security_information_t secInfo; + // UE DL Aggregate Maximum Bit Rate (M) + long ueDlAggMaxBitRate; + // UE DL Maximum Integrity Protected Data Rate (O) + long *ueDlMaxIPBitRate; + // Serving PLMN (M) PLMN_ID_t servingPLMNid; + // Bearer Context Status Change (O) BEARER_CONTEXT_STATUS_t bearerContextStatus; + // UE Inactivity Timer (O) + int *inactivityTimerUE; + // NG-RAN PDU Session Resource To Setup List (M) int numPDUSessions; pdu_session_to_setup_t pduSession[E1AP_MAX_NUM_PDU_SESSIONS]; +} e1ap_bearer_setup_req_t; + +/** + * Bearer Context Modification Request, clause 9.2.2.4 of 3GPP TS 38.463 + */ +typedef struct e1ap_bearer_mod_req_s { + // gNB-CU-CP UE E1AP ID (M) + uint32_t gNB_cu_cp_ue_id; + // gNB-CU-UP UE E1AP ID (M) + uint32_t gNB_cu_up_ue_id; + // Security Information (O) + security_information_t *secInfo; + // UE DL Aggregate Maximum Bit Rate (O) + uint32_t *ueDlAggMaxBitRate; + // UE DL Maximum Integrity Protected Data Rate (O) + uint32_t *ueDlMaxIPBitRate; + // Bearer Context Status Change (O) + BEARER_CONTEXT_STATUS_t *bearerContextStatus; + // UE Inactivity Timer (O) + int *inactivityTimer; + // Data Discard Required (O) + bool dataDiscardRequired; + // NG-RAN PDU Session Resource To Setup List (O) + int numPDUSessions; + pdu_session_to_setup_t pduSession[E1AP_MAX_NUM_PDU_SESSIONS]; + // NG-RAN PDU Session Resource To Modify List (O) int numPDUSessionsMod; pdu_session_to_mod_t pduSessionMod[E1AP_MAX_NUM_PDU_SESSIONS]; -} e1ap_bearer_setup_req_t, e1ap_bearer_mod_req_t; +} e1ap_bearer_mod_req_t; typedef struct e1ap_bearer_release_cmd_s { uint32_t gNB_cu_cp_ue_id; @@ -425,43 +517,61 @@ typedef struct e1ap_bearer_release_cplt_s { uint32_t gNB_cu_up_ue_id; } e1ap_bearer_release_cplt_t; -typedef struct qos_flow_setup_s { - long qfi; -} qos_flow_setup_t; - typedef struct DRB_nGRAN_setup_s { + // DRB ID (M) long id; int numUpParam; up_params_t UpParamList[E1AP_MAX_NUM_UP_PARAM]; + // Flow Setup List (M) int numQosFlowSetup; - qos_flow_setup_t qosFlows[E1AP_MAX_NUM_QOS_FLOWS]; + qos_flow_list_t qosFlows[E1AP_MAX_NUM_QOS_FLOWS]; } DRB_nGRAN_setup_t; +/* DRB Modified Item */ typedef struct DRB_nGRAN_modified_s { + // DRB ID (M) long id; + // UL UP Parameters (O) + int numUpParam; + up_params_t ul_UP_Params[E1AP_MAX_NUM_UP_PARAM]; + // Flow Setup List (O) + int numQosFlowSetup; + qos_flow_list_t qosFlows[E1AP_MAX_NUM_QOS_FLOWS]; + // Old QoS Flow List (O) + int numOldQosFlow; + qos_flow_list_t oldQosFlows[E1AP_MAX_NUM_QOS_FLOWS]; } DRB_nGRAN_modified_t; typedef struct DRB_nGRAN_failed_s { long id; - long cause_type; - long cause; + e1ap_cause_t cause; } DRB_nGRAN_failed_t; typedef struct pdu_session_setup_s { long id; - in_addr_t tlAddress; - long teId; + // Transport layer info + UP_TL_information_t tl_info; int numDRBSetup; DRB_nGRAN_setup_t DRBnGRanList[E1AP_MAX_NUM_DRBS]; int numDRBFailed; DRB_nGRAN_failed_t DRBnGRanFailedList[E1AP_MAX_NUM_DRBS]; } pdu_session_setup_t; +/* PDU Session Resource Modified List (9.3.3.17) */ typedef struct pdu_session_modif_s { + // PDU Session ID (M) long id; - // setup as part of PDU session modification not supported yet + // Security Result (O) + e1ap_indication_t *integrityProtectionIndication; + e1ap_indication_t *confidentialityProtectionIndication; + // NG DL UP Transport Layer Information (O) + UP_TL_information_t *ng_DL_UP_TL_info; + // DRB Modified List (O) int numDRBModified; DRB_nGRAN_modified_t DRBnGRanModList[E1AP_MAX_NUM_DRBS]; + // DRB Setup List (O) + int numDRBSetup; + DRB_nGRAN_setup_t DRBnGRanSetupList[E1AP_MAX_NUM_DRBS]; } pdu_session_modif_t; typedef struct e1ap_bearer_setup_resp_s { @@ -472,8 +582,11 @@ typedef struct e1ap_bearer_setup_resp_s { } e1ap_bearer_setup_resp_t; typedef struct e1ap_bearer_modif_resp_s { + // gNB-CU-CP UE E1AP ID (M) uint32_t gNB_cu_cp_ue_id; + // gNB-CU-UP UE E1AP ID (M) uint32_t gNB_cu_up_ue_id; + // NG-RAN PDU Session Resource Modified List (O) int numPDUSessionsMod; pdu_session_modif_t pduSessionMod[E1AP_MAX_NUM_PDU_SESSIONS]; } e1ap_bearer_modif_resp_t; diff --git a/openair2/E1AP/e1ap.c b/openair2/E1AP/e1ap.c index e9aa7f54df..72eff658cb 100644 --- a/openair2/E1AP/e1ap.c +++ b/openair2/E1AP/e1ap.c @@ -429,7 +429,7 @@ int e1apCUCP_handle_BEARER_CONTEXT_SETUP_FAILURE(sctp_assoc_t assoc_id, e1ap_upc BEARER CONTEXT MODIFICATION REQUEST */ -static int fill_BEARER_CONTEXT_MODIFICATION_REQUEST(e1ap_bearer_setup_req_t *const bearerCxt, E1AP_E1AP_PDU_t *pdu) +static int fill_BEARER_CONTEXT_MODIFICATION_REQUEST(e1ap_bearer_mod_req_t *const bearerCxt, E1AP_E1AP_PDU_t *pdu) { pdu->present = E1AP_E1AP_PDU_PR_initiatingMessage; asn1cCalloc(pdu->choice.initiatingMessage, msg); @@ -473,7 +473,7 @@ static int fill_BEARER_CONTEXT_MODIFICATION_REQUEST(e1ap_bearer_setup_req_t *con asn1cSequenceAdd(drb2Mod_List->list, E1AP_DRB_To_Modify_Item_NG_RAN_t, drb2Mod); drb2Mod->dRB_ID = j->id; asn1cCalloc(drb2Mod->pDCP_Configuration, pDCP_Configuration); - if (j->pdcp_config.pDCP_Reestablishment) { + if (j->pdcp_config && j->pdcp_config->pDCP_Reestablishment) { asn1cCallocOne(pDCP_Configuration->pDCP_Reestablishment, E1AP_PDCP_Reestablishment_true); } if (j->numDlUpParam > 0) { @@ -482,8 +482,8 @@ static int fill_BEARER_CONTEXT_MODIFICATION_REQUEST(e1ap_bearer_setup_req_t *con asn1cSequenceAdd(DL_UP_Param_List->list, E1AP_UP_Parameters_Item_t, DL_UP_Param); DL_UP_Param->uP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; asn1cCalloc(DL_UP_Param->uP_TNL_Information.choice.gTPTunnel, gTPTunnel); - TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(k->tlAddress, &gTPTunnel->transportLayerAddress); - INT32_TO_OCTET_STRING(k->teId, &gTPTunnel->gTP_TEID); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(k->tl_info.tlAddress, &gTPTunnel->transportLayerAddress); + INT32_TO_OCTET_STRING(k->tl_info.teId, &gTPTunnel->gTP_TEID); DL_UP_Param->cell_Group_ID = k->cell_group_id; } @@ -491,7 +491,7 @@ static int fill_BEARER_CONTEXT_MODIFICATION_REQUEST(e1ap_bearer_setup_req_t *con } } /* c4. E1AP_ProtocolIE_ID_id_BearerContextStatusChange */ - if (bearerCxt->bearerContextStatus == BEARER_SUSPEND) { + if (bearerCxt->bearerContextStatus && *bearerCxt->bearerContextStatus == BEARER_SUSPEND) { asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ieC4); ieC4->id = E1AP_ProtocolIE_ID_id_BearerContextStatusChange; ieC4->criticality = E1AP_Criticality_reject; @@ -635,7 +635,8 @@ static void extract_BEARER_CONTEXT_MODIFICATION_REQUEST(const E1AP_E1AP_PDU_t *p if (drb2Mod->pDCP_Configuration != NULL && drb2Mod->pDCP_Configuration->pDCP_Reestablishment != NULL && *drb2Mod->pDCP_Configuration->pDCP_Reestablishment == E1AP_PDCP_Reestablishment_true) { - drb->pdcp_config.pDCP_Reestablishment = true; + drb->pdcp_config = malloc_or_fail(sizeof(*drb->pdcp_config)); + drb->pdcp_config->pDCP_Reestablishment = true; } if (drb2Mod->dL_UP_Parameters) { /* Optional IE in the DRB To Modify Item */ E1AP_UP_Parameters_t *dl_up_paramList = drb2Mod->dL_UP_Parameters; @@ -647,8 +648,8 @@ static void extract_BEARER_CONTEXT_MODIFICATION_REQUEST(const E1AP_E1AP_PDU_t *p DevAssert(dl_up_param_in->uP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel); BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4( &dl_up_param_in->uP_TNL_Information.choice.gTPTunnel->transportLayerAddress, - dl_up_param->tlAddress); - OCTET_STRING_TO_INT32(&dl_up_param_in->uP_TNL_Information.choice.gTPTunnel->gTP_TEID, dl_up_param->teId); + dl_up_param->tl_info.tlAddress); + OCTET_STRING_TO_INT32(&dl_up_param_in->uP_TNL_Information.choice.gTPTunnel->gTP_TEID, dl_up_param->tl_info.teId); } else { AssertFatal(false, "gTPTunnel IE is missing. It is mandatory at this point\n"); } @@ -663,7 +664,8 @@ static void extract_BEARER_CONTEXT_MODIFICATION_REQUEST(const E1AP_E1AP_PDU_t *p /* Bearer Context Status Change */ DevAssert(ie->criticality == E1AP_Criticality_reject); DevAssert(ie->value.present == E1AP_BearerContextModificationRequestIEs__value_PR_BearerContextStatusChange); - bearerCxt->bearerContextStatus = (ie->value.choice.BearerContextStatusChange == E1AP_BearerContextStatusChange_suspend) ? BEARER_SUSPEND : BEARER_ACTIVE; + bearerCxt->bearerContextStatus = malloc_or_fail(*bearerCxt->bearerContextStatus); + *bearerCxt->bearerContextStatus = (ie->value.choice.BearerContextStatusChange == E1AP_BearerContextStatusChange_suspend) ? BEARER_SUSPEND : BEARER_ACTIVE; break; default: diff --git a/openair2/E1AP/lib/e1ap_bearer_context_management.c b/openair2/E1AP/lib/e1ap_bearer_context_management.c index 2552e478ba..d73a3bddd7 100644 --- a/openair2/E1AP/lib/e1ap_bearer_context_management.c +++ b/openair2/E1AP/lib/e1ap_bearer_context_management.c @@ -37,9 +37,130 @@ #include "e1ap_bearer_context_management.h" #include "e1ap_lib_includes.h" -/* ==================================== - * E1AP Bearer Context Setup Request - * ==================================== */ +/** @brief Encode E1AP_SecurityIndication_t (3GPP TS 38.463 9.3.1.23) */ +static E1AP_SecurityIndication_t e1_encode_security_indication(const security_indication_t *in) +{ + E1AP_SecurityIndication_t out = {0}; + out.integrityProtectionIndication = in->integrityProtectionIndication; + out.confidentialityProtectionIndication = in->confidentialityProtectionIndication; + if (in->integrityProtectionIndication != SECURITY_NOT_NEEDED) { + asn1cCalloc(out.maximumIPdatarate, ipDataRate); + ipDataRate->maxIPrate = in->maxIPrate; + } + return out; +} + +/** @brief Decode E1AP_SecurityIndication_t (3GPP TS 38.463 9.3.1.23) */ +static bool e1_decode_security_indication(security_indication_t *out, const E1AP_SecurityIndication_t *in) +{ + // Confidentiality Protection Indication (M) + out->confidentialityProtectionIndication = in->confidentialityProtectionIndication; + // Maximum Integrity Protected Data Rate (Conditional) + if (in->integrityProtectionIndication != SECURITY_NOT_NEEDED) { + if (in->maximumIPdatarate) + out->maxIPrate = in->maximumIPdatarate->maxIPrate; + else { + PRINT_ERROR("Received integrityProtectionIndication but maximumIPdatarate IE is missing\n"); + return false; + } + } + return true; +} + +/** + * @brief Encode E1AP_SecurityInformation_t + */ +static E1AP_SecurityInformation_t e1_encode_security_info(const security_information_t *secInfo) +{ + E1AP_SecurityInformation_t ie = {0}; + E1AP_SecurityAlgorithm_t *securityAlgorithm = &ie.securityAlgorithm; + E1AP_UPSecuritykey_t *uPSecuritykey = &ie.uPSecuritykey; + securityAlgorithm->cipheringAlgorithm = secInfo->cipheringAlgorithm; + OCTET_STRING_fromBuf(&uPSecuritykey->encryptionKey, secInfo->encryptionKey, E1AP_SECURITY_KEY_SIZE); + asn1cCallocOne(securityAlgorithm->integrityProtectionAlgorithm, secInfo->integrityProtectionAlgorithm); + asn1cCalloc(uPSecuritykey->integrityProtectionKey, protKey); + OCTET_STRING_fromBuf(protKey, secInfo->integrityProtectionKey, E1AP_SECURITY_KEY_SIZE); + return ie; +} + +/** + * @brief Decode E1AP_SecurityInformation_t + */ +static bool e1_decode_security_info(security_information_t *out, const E1AP_SecurityInformation_t *in) +{ + out->cipheringAlgorithm = in->securityAlgorithm.cipheringAlgorithm; + memcpy(out->encryptionKey, in->uPSecuritykey.encryptionKey.buf, in->uPSecuritykey.encryptionKey.size); + if (in->securityAlgorithm.integrityProtectionAlgorithm) + out->integrityProtectionAlgorithm = *in->securityAlgorithm.integrityProtectionAlgorithm; + if (in->uPSecuritykey.integrityProtectionKey) { + E1AP_IntegrityProtectionKey_t *ipKey = in->uPSecuritykey.integrityProtectionKey; + memcpy(out->integrityProtectionKey, ipKey->buf, ipKey->size); + } + return true; +} + +/** + * @brief Encode E1AP_UP_TNL_Information_t + */ +static E1AP_UP_TNL_Information_t e1_encode_up_tnl_info(const UP_TL_information_t *in) +{ + E1AP_UP_TNL_Information_t out = {0}; + out.present = E1AP_UP_TNL_Information_PR_gTPTunnel; + asn1cCalloc(out.choice.gTPTunnel, gTPTunnel); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(in->tlAddress, &gTPTunnel->transportLayerAddress); + INT32_TO_OCTET_STRING(in->teId, &gTPTunnel->gTP_TEID); + return out; +} + +/** + * @brief Decode E1AP_UP_TNL_Information_t + */ +static bool e1_decode_up_tnl_info(UP_TL_information_t *out, const E1AP_UP_TNL_Information_t *in) +{ + /* NG UL UP Transport Layer Information (M) (9.3.2.1 of 3GPP TS 38.463) */ + // GTP Tunnel + struct E1AP_GTPTunnel *gTPTunnel = in->choice.gTPTunnel; + AssertFatal(gTPTunnel != NULL, "item->nG_UL_UP_TNL_Information.choice.gTPTunnel is a mandatory IE"); + _E1_EQ_CHECK_INT(in->present, E1AP_UP_TNL_Information_PR_gTPTunnel); + BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&gTPTunnel->transportLayerAddress, out->tlAddress); + // GTP-TEID + OCTET_STRING_TO_INT32(&gTPTunnel->gTP_TEID, out->teId); + return true; +} + +/** @brief Encode PDCP Configuration IE */ +static E1AP_PDCP_Configuration_t e1_encode_pdcp_config(const bearer_context_pdcp_config_t *in) +{ + E1AP_PDCP_Configuration_t out = {0}; + out.pDCP_SN_Size_UL = in->pDCP_SN_Size_UL; + out.pDCP_SN_Size_DL = in->pDCP_SN_Size_DL; + asn1cCallocOne(out.discardTimer, in->discardTimer); + E1AP_T_ReorderingTimer_t *roTimer = calloc_or_fail(1, sizeof(*roTimer)); + out.t_ReorderingTimer = roTimer; + roTimer->t_Reordering = in->reorderingTimer; + out.rLC_Mode = in->rLC_Mode; + if (in->pDCP_Reestablishment) { + asn1cCallocOne(out.pDCP_Reestablishment, E1AP_PDCP_Reestablishment_true); + } + return out; +} + +/** @brief Decode PDCP Configuration IE */ +static bool e1_decode_pdcp_config(bearer_context_pdcp_config_t *out, const E1AP_PDCP_Configuration_t *in) +{ + out->pDCP_SN_Size_UL = in->pDCP_SN_Size_UL; + out->pDCP_SN_Size_DL = in->pDCP_SN_Size_DL; + if (in->discardTimer) { + out->discardTimer = *in->discardTimer; + } + if (in->t_ReorderingTimer) { + out->reorderingTimer = in->t_ReorderingTimer->t_Reordering; + } + out->rLC_Mode = in->rLC_Mode; + if (in->pDCP_Reestablishment && *in->pDCP_Reestablishment == E1AP_PDCP_Reestablishment_true) + out->pDCP_Reestablishment = true; + return true; +} /** * @brief Equality check for bearer_context_pdcp_config_t @@ -55,6 +176,25 @@ static bool eq_pdcp_config(const bearer_context_pdcp_config_t *a, const bearer_c return true; } +/** @brief Encode SDAP Configuration IE */ +static E1AP_SDAP_Configuration_t e1_encode_sdap_config(const bearer_context_sdap_config_t *in) +{ + E1AP_SDAP_Configuration_t out; + out.defaultDRB = in->defaultDRB ? E1AP_DefaultDRB_true : E1AP_DefaultDRB_false; + out.sDAP_Header_UL = in->sDAP_Header_UL; + out.sDAP_Header_DL = in->sDAP_Header_DL; + return out; +} + +/** @brief Decode SDAP Configuration IE */ +static bool e1_decode_sdap_config(bearer_context_sdap_config_t *out, const E1AP_SDAP_Configuration_t *in) +{ + out->defaultDRB = in->defaultDRB == E1AP_DefaultDRB_true; + out->sDAP_Header_UL = in->sDAP_Header_UL; + out->sDAP_Header_DL = in->sDAP_Header_DL; + return true; +} + /** * @brief Equality check for bearer_context_sdap_config_t */ @@ -66,6 +206,69 @@ static bool eq_sdap_config(const bearer_context_sdap_config_t *a, const bearer_c return true; } +static E1AP_QoS_Flow_QoS_Parameter_Item_t e1_encode_qos_flow_to_setup(const qos_flow_to_setup_t *in) +{ + E1AP_QoS_Flow_QoS_Parameter_Item_t out = {0}; + out.qoS_Flow_Identifier = in->qfi; + // QoS Characteristics + const qos_characteristics_t *qos_char_in = &in->qos_params.qos_characteristics; + if (qos_char_in->qos_type == NON_DYNAMIC) { // non Dynamic 5QI + out.qoSFlowLevelQoSParameters.qoS_Characteristics.present = E1AP_QoS_Characteristics_PR_non_Dynamic_5QI; + asn1cCalloc(out.qoSFlowLevelQoSParameters.qoS_Characteristics.choice.non_Dynamic_5QI, non_Dynamic_5QI); + non_Dynamic_5QI->fiveQI = qos_char_in->non_dynamic.fiveqi; + } else { // dynamic 5QI + out.qoSFlowLevelQoSParameters.qoS_Characteristics.present = E1AP_QoS_Characteristics_PR_dynamic_5QI; + asn1cCalloc(out.qoSFlowLevelQoSParameters.qoS_Characteristics.choice.dynamic_5QI, dynamic_5QI); + dynamic_5QI->qoSPriorityLevel = qos_char_in->dynamic.qos_priority_level; + dynamic_5QI->packetDelayBudget = qos_char_in->dynamic.packet_delay_budget; + dynamic_5QI->packetErrorRate.pER_Scalar = qos_char_in->dynamic.packet_error_rate.per_scalar; + dynamic_5QI->packetErrorRate.pER_Exponent = qos_char_in->dynamic.packet_error_rate.per_exponent; + } + // QoS Retention Priority + const ngran_allocation_retention_priority_t *rent_priority_in = &in->qos_params.alloc_reten_priority; + E1AP_NGRANAllocationAndRetentionPriority_t *arp = &out.qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority; + arp->priorityLevel = rent_priority_in->priority_level; + arp->pre_emptionCapability = rent_priority_in->preemption_capability; + arp->pre_emptionVulnerability = rent_priority_in->preemption_vulnerability; + return out; +} + +bool e1_decode_qos_flow_to_setup(qos_flow_to_setup_t *out, const E1AP_QoS_Flow_QoS_Parameter_Item_t *in) +{ + // QoS Flow Identifier (M) + out->qfi = in->qoS_Flow_Identifier; + qos_characteristics_t *qos_char = &out->qos_params.qos_characteristics; + // QoS Flow Level QoS Parameters (M) + const E1AP_QoSFlowLevelQoSParameters_t *qosParams = &in->qoSFlowLevelQoSParameters; + const E1AP_QoS_Characteristics_t *qoS_Characteristics = &qosParams->qoS_Characteristics; + switch (qoS_Characteristics->present) { + case E1AP_QoS_Characteristics_PR_non_Dynamic_5QI: + qos_char->qos_type = NON_DYNAMIC; + qos_char->non_dynamic.fiveqi = qoS_Characteristics->choice.non_Dynamic_5QI->fiveQI; + break; + case E1AP_QoS_Characteristics_PR_dynamic_5QI: { + E1AP_Dynamic5QIDescriptor_t *dynamic5QI = qoS_Characteristics->choice.dynamic_5QI; + qos_char->qos_type = DYNAMIC; + qos_char->dynamic.qos_priority_level = dynamic5QI->qoSPriorityLevel; + qos_char->dynamic.packet_delay_budget = dynamic5QI->packetDelayBudget; + qos_char->dynamic.packet_error_rate.per_scalar = dynamic5QI->packetErrorRate.pER_Scalar; + qos_char->dynamic.packet_error_rate.per_exponent = dynamic5QI->packetErrorRate.pER_Exponent; + break; + } + default: + PRINT_ERROR("Unexpected QoS Characteristics type: %d\n", qoS_Characteristics->present); + return false; + break; + } + // NG-RAN Allocation and Retention Priority (M) + ngran_allocation_retention_priority_t *rent_priority = &out->qos_params.alloc_reten_priority; + const E1AP_NGRANAllocationAndRetentionPriority_t *aRP = &qosParams->nGRANallocationRetentionPriority; + rent_priority->priority_level = aRP->priorityLevel; + rent_priority->preemption_capability = aRP->pre_emptionCapability; + rent_priority->preemption_vulnerability = aRP->pre_emptionVulnerability; + return true; +} + /** * @brief Equality check for qos_flow_to_setup_t */ @@ -110,36 +313,12 @@ static bool eq_drb_to_setup(const DRB_nGRAN_to_setup_t *a, const DRB_nGRAN_to_se return result; } -/** - * @brief Equality check for DRB_nGRAN_to_mod_t - */ -static bool eq_drb_to_mod(const DRB_nGRAN_to_mod_t *a, const DRB_nGRAN_to_mod_t *b) +static void free_drb_to_setup_item(const DRB_nGRAN_to_setup_t *msg) { - bool result = true; - _E1_EQ_CHECK_LONG(a->id, b->id); - _E1_EQ_CHECK_INT(a->numCellGroups, b->numCellGroups); - for (int i = 0; i < a->numCellGroups; i++) { - _E1_EQ_CHECK_INT(a->cellGroupList[i], b->cellGroupList[i]); - } - _E1_EQ_CHECK_INT(a->numQosFlow2Setup, b->numQosFlow2Setup); - for (int i = 0; i < a->numQosFlow2Setup; i++) { - result &= eq_qos_flow(&a->qosFlows[i], &b->qosFlows[i]); - } - _E1_EQ_CHECK_INT(a->tlAddress, b->tlAddress); - _E1_EQ_CHECK_INT(a->teId, b->teId); - _E1_EQ_CHECK_INT(a->numDlUpParam, b->numDlUpParam); - for (int i = 0; i < a->numDlUpParam; i++) { - _E1_EQ_CHECK_INT(a->DlUpParamList[i].cell_group_id, b->DlUpParamList[i].cell_group_id); - _E1_EQ_CHECK_LONG(a->DlUpParamList[i].teId, b->DlUpParamList[i].teId); - _E1_EQ_CHECK_INT(a->DlUpParamList[i].tlAddress, b->DlUpParamList[i].tlAddress); - } - result &= eq_pdcp_config(&a->pdcp_config, &b->pdcp_config); - result &= eq_sdap_config(&a->sdap_config, &b->sdap_config); - return result; + free(msg->drb_inactivity_timer); } -/* PDU Session To Setup Item */ - +/** @brief Encode PDU Session Resource To Setup List Item (3GPP TS 38.463 9.3.3.2) */ static bool e1_encode_pdu_session_to_setup_item(E1AP_PDU_Session_Resource_To_Setup_Item_t *item, const pdu_session_to_setup_t *in) { item->pDU_Session_ID = in->sessionId; @@ -150,73 +329,33 @@ static bool e1_encode_pdu_session_to_setup_item(E1AP_PDU_Session_Resource_To_Set item->sNSSAI.sD = malloc_or_fail(sizeof(*item->sNSSAI.sD)); INT24_TO_OCTET_STRING(in->nssai.sd, item->sNSSAI.sD); } - // Security Indication - E1AP_SecurityIndication_t *siOut = &item->securityIndication; - const security_indication_t *siIn = &in->securityIndication; - siOut->integrityProtectionIndication = siIn->integrityProtectionIndication; - siOut->confidentialityProtectionIndication = siIn->confidentialityProtectionIndication; - if (siIn->integrityProtectionIndication != SECURITY_NOT_NEEDED) { - asn1cCalloc(siOut->maximumIPdatarate, ipDataRate); - ipDataRate->maxIPrate = siIn->maxIPrate; - } - // TNL Information - item->nG_UL_UP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; - asn1cCalloc(item->nG_UL_UP_TNL_Information.choice.gTPTunnel, gTPTunnel); - const UP_TL_information_t *UP_TL_information = &in->UP_TL_information; - TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(UP_TL_information->tlAddress, &gTPTunnel->transportLayerAddress); - INT32_TO_OCTET_STRING(UP_TL_information->teId, &gTPTunnel->gTP_TEID); - // DRB to setup + // Security Indication (M) + item->securityIndication = e1_encode_security_indication(&in->securityIndication); + // NG UL UP Transport Layer Information (M) + item->nG_UL_UP_TNL_Information = e1_encode_up_tnl_info(&in->UP_TL_information); + // DRB To Setup List (M) for (const DRB_nGRAN_to_setup_t *j = in->DRBnGRanList; j < in->DRBnGRanList + in->numDRB2Setup; j++) { asn1cSequenceAdd(item->dRB_To_Setup_List_NG_RAN.list, E1AP_DRB_To_Setup_Item_NG_RAN_t, ieC6_1_1); ieC6_1_1->dRB_ID = j->id; - // SDAP Config - E1AP_SDAP_Configuration_t *sdap = &ieC6_1_1->sDAP_Configuration; - sdap->defaultDRB = j->sdap_config.defaultDRB ? E1AP_DefaultDRB_true : E1AP_DefaultDRB_false; - sdap->sDAP_Header_UL = j->sdap_config.sDAP_Header_UL; - sdap->sDAP_Header_DL = j->sdap_config.sDAP_Header_DL; - // PDCP Config - E1AP_PDCP_Configuration_t *pdcp = &ieC6_1_1->pDCP_Configuration; - pdcp->pDCP_SN_Size_UL = j->pdcp_config.pDCP_SN_Size_UL; - pdcp->pDCP_SN_Size_DL = j->pdcp_config.pDCP_SN_Size_DL; - asn1cCallocOne(pdcp->discardTimer, j->pdcp_config.discardTimer); - E1AP_T_ReorderingTimer_t *roTimer = calloc_or_fail(1, sizeof(*roTimer)); - pdcp->t_ReorderingTimer = roTimer; - roTimer->t_Reordering = j->pdcp_config.reorderingTimer; - pdcp->rLC_Mode = j->pdcp_config.rLC_Mode; - // Cell Group + // SDAP Configuration (M) + ieC6_1_1->sDAP_Configuration = e1_encode_sdap_config(&j->sdap_config); + // PDCP Configuration (M) + ieC6_1_1->pDCP_Configuration = e1_encode_pdcp_config(&j->pdcp_config); + // Cell Group Information (M) for (const cell_group_id_t *k = j->cellGroupList; k < j->cellGroupList + j->numCellGroups; k++) { asn1cSequenceAdd(ieC6_1_1->cell_Group_Information.list, E1AP_Cell_Group_Information_Item_t, ieC6_1_1_1); ieC6_1_1_1->cell_Group_ID = *k; } // QoS Flows for (const qos_flow_to_setup_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlow2Setup; k++) { - asn1cSequenceAdd(ieC6_1_1->qos_flow_Information_To_Be_Setup, E1AP_QoS_Flow_QoS_Parameter_Item_t, ieC6_1_1_1); - ieC6_1_1_1->qoS_Flow_Identifier = k->qfi; - // QoS Characteristics - const qos_characteristics_t *qos_char_in = &k->qos_params.qos_characteristics; - if (qos_char_in->qos_type == NON_DYNAMIC) { // non Dynamic 5QI - ieC6_1_1_1->qoSFlowLevelQoSParameters.qoS_Characteristics.present = E1AP_QoS_Characteristics_PR_non_Dynamic_5QI; - asn1cCalloc(ieC6_1_1_1->qoSFlowLevelQoSParameters.qoS_Characteristics.choice.non_Dynamic_5QI, non_Dynamic_5QI); - non_Dynamic_5QI->fiveQI = qos_char_in->non_dynamic.fiveqi; - } else { // dynamic 5QI - ieC6_1_1_1->qoSFlowLevelQoSParameters.qoS_Characteristics.present = E1AP_QoS_Characteristics_PR_dynamic_5QI; - asn1cCalloc(ieC6_1_1_1->qoSFlowLevelQoSParameters.qoS_Characteristics.choice.dynamic_5QI, dynamic_5QI); - dynamic_5QI->qoSPriorityLevel = qos_char_in->dynamic.qos_priority_level; - dynamic_5QI->packetDelayBudget = qos_char_in->dynamic.packet_delay_budget; - dynamic_5QI->packetErrorRate.pER_Scalar = qos_char_in->dynamic.packet_error_rate.per_scalar; - dynamic_5QI->packetErrorRate.pER_Exponent = qos_char_in->dynamic.packet_error_rate.per_exponent; - } - // QoS Retention Priority - const ngran_allocation_retention_priority_t *rent_priority_in = &k->qos_params.alloc_reten_priority; - E1AP_NGRANAllocationAndRetentionPriority_t *arp = &ieC6_1_1_1->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority; - arp->priorityLevel = rent_priority_in->priority_level; - arp->pre_emptionCapability = rent_priority_in->preemption_capability; - arp->pre_emptionVulnerability = rent_priority_in->preemption_vulnerability; + asn1cSequenceAdd(ieC6_1_1->qos_flow_Information_To_Be_Setup.list, E1AP_QoS_Flow_QoS_Parameter_Item_t, ieC6_1_1_2); + *ieC6_1_1_2 = e1_encode_qos_flow_to_setup(k); } } return true; } +/** @brief Decode PDU Session Resource To Setup List Item (3GPP TS 38.463 9.3.3.2) */ static bool e1_decode_pdu_session_to_setup_item(pdu_session_to_setup_t *out, E1AP_PDU_Session_Resource_To_Setup_Item_t *item) { // PDU Session ID (M) @@ -230,22 +369,8 @@ static bool e1_decode_pdu_session_to_setup_item(pdu_session_to_setup_t *out, E1A // SD (O) if (item->sNSSAI.sD != NULL) OCTET_STRING_TO_INT24(item->sNSSAI.sD, out->nssai.sd); - /* Security Indication (M) */ - E1AP_SecurityIndication_t *securityIndication = &item->securityIndication; - // Integrity Protection Indication (M) - security_indication_t *sec = &out->securityIndication; - sec->integrityProtectionIndication = securityIndication->integrityProtectionIndication; - // Confidentiality Protection Indication (M) - sec->confidentialityProtectionIndication = securityIndication->confidentialityProtectionIndication; - // Maximum Integrity Protected Data Rate (Conditional) - if (sec->integrityProtectionIndication != SECURITY_NOT_NEEDED) { - if (securityIndication->maximumIPdatarate) - sec->maxIPrate = securityIndication->maximumIPdatarate->maxIPrate; - else { - PRINT_ERROR("Received integrityProtectionIndication but maximumIPdatarate IE is missing\n"); - return false; - } - } + // Security Indication (M) + CHECK_E1AP_DEC(e1_decode_security_indication(&out->securityIndication, &item->securityIndication)); /* NG UL UP Transport Layer Information (M) (9.3.2.1 of 3GPP TS 38.463) */ // GTP Tunnel struct E1AP_GTPTunnel *gTPTunnel = item->nG_UL_UP_TNL_Information.choice.gTPTunnel; @@ -254,38 +379,22 @@ static bool e1_decode_pdu_session_to_setup_item(pdu_session_to_setup_t *out, E1A return false; } _E1_EQ_CHECK_INT(item->nG_UL_UP_TNL_Information.present, E1AP_UP_TNL_Information_PR_gTPTunnel); - // Transport Layer Address - UP_TL_information_t *UP_TL_information = &out->UP_TL_information; - BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&gTPTunnel->transportLayerAddress, UP_TL_information->tlAddress); - // GTP-TEID - OCTET_STRING_TO_INT32(&gTPTunnel->gTP_TEID, UP_TL_information->teId); - // DRB To Setup List ( > 1 item ) + CHECK_E1AP_DEC(e1_decode_up_tnl_info(&out->UP_TL_information, &item->nG_UL_UP_TNL_Information)); + /* DRB To Setup List ( > 1 item ) */ E1AP_DRB_To_Setup_List_NG_RAN_t *drb2SetupList = &item->dRB_To_Setup_List_NG_RAN; _E1_EQ_CHECK_GENERIC(drb2SetupList->list.count > 0, "%d", drb2SetupList->list.count, 0); out->numDRB2Setup = drb2SetupList->list.count; - _E1_EQ_CHECK_LONG(out->numDRB2Setup, 1UL); // can only handle one DRB per PDU session + _E1_EQ_CHECK_INT(out->numDRB2Setup, 1); // can only handle one DRB per PDU session for (int j = 0; j < drb2SetupList->list.count; j++) { DRB_nGRAN_to_setup_t *drb = out->DRBnGRanList + j; E1AP_DRB_To_Setup_Item_NG_RAN_t *drb2Setup = drb2SetupList->list.array[j]; // DRB ID (M) drb->id = drb2Setup->dRB_ID; - // >SDAP Configuration (M) - E1AP_SDAP_Configuration_t *sdap = &drb2Setup->sDAP_Configuration; - drb->sdap_config.defaultDRB = sdap->defaultDRB == E1AP_DefaultDRB_true; - drb->sdap_config.sDAP_Header_UL = sdap->sDAP_Header_UL; - drb->sdap_config.sDAP_Header_DL = sdap->sDAP_Header_DL; + // SDAP Configuration (M) + CHECK_E1AP_DEC(e1_decode_sdap_config(&drb->sdap_config, &drb2Setup->sDAP_Configuration)); // PDCP Configuration (M) - E1AP_PDCP_Configuration_t *pdcp = &drb2Setup->pDCP_Configuration; - drb->pdcp_config.pDCP_SN_Size_UL = pdcp->pDCP_SN_Size_UL; - drb->pdcp_config.pDCP_SN_Size_DL = pdcp->pDCP_SN_Size_DL; - if (pdcp->discardTimer) { - drb->pdcp_config.discardTimer = *pdcp->discardTimer; - } - if (pdcp->t_ReorderingTimer) { - drb->pdcp_config.reorderingTimer = pdcp->t_ReorderingTimer->t_Reordering; - } - drb->pdcp_config.rLC_Mode = pdcp->rLC_Mode; - // Cell Group Information (M) ( > 1 item ) + CHECK_E1AP_DEC(e1_decode_pdcp_config(&drb->pdcp_config, &drb2Setup->pDCP_Configuration)); + // Cell Group Information (M) E1AP_Cell_Group_Information_t *cellGroupList = &drb2Setup->cell_Group_Information; _E1_EQ_CHECK_GENERIC(cellGroupList->list.count > 0, "%d", cellGroupList->list.count, 0); drb->numCellGroups = cellGroupList->list.count; @@ -298,44 +407,42 @@ static bool e1_decode_pdu_session_to_setup_item(pdu_session_to_setup_t *out, E1A E1AP_QoS_Flow_QoS_Parameter_List_t *qos2SetupList = &drb2Setup->qos_flow_Information_To_Be_Setup; drb->numQosFlow2Setup = qos2SetupList->list.count; for (int k = 0; k < qos2SetupList->list.count; k++) { - qos_flow_to_setup_t *qos_flow = drb->qosFlows + k; - E1AP_QoS_Flow_QoS_Parameter_Item_t *qos2Setup = qos2SetupList->list.array[k]; - // QoS Flow Identifier (M) - qos_flow->qfi = qos2Setup->qoS_Flow_Identifier; - qos_characteristics_t *qos_char = &qos_flow->qos_params.qos_characteristics; - // QoS Flow Level QoS Parameters (M) - E1AP_QoSFlowLevelQoSParameters_t *qosParams = &qos2Setup->qoSFlowLevelQoSParameters; - E1AP_QoS_Characteristics_t *qoS_Characteristics = &qosParams->qoS_Characteristics; - switch (qoS_Characteristics->present) { - case E1AP_QoS_Characteristics_PR_non_Dynamic_5QI: - qos_char->qos_type = NON_DYNAMIC; - qos_char->non_dynamic.fiveqi = qoS_Characteristics->choice.non_Dynamic_5QI->fiveQI; - break; - case E1AP_QoS_Characteristics_PR_dynamic_5QI: { - E1AP_Dynamic5QIDescriptor_t *dynamic5QI = qoS_Characteristics->choice.dynamic_5QI; - qos_char->qos_type = DYNAMIC; - qos_char->dynamic.qos_priority_level = dynamic5QI->qoSPriorityLevel; - qos_char->dynamic.packet_delay_budget = dynamic5QI->packetDelayBudget; - qos_char->dynamic.packet_error_rate.per_scalar = dynamic5QI->packetErrorRate.pER_Scalar; - qos_char->dynamic.packet_error_rate.per_exponent = dynamic5QI->packetErrorRate.pER_Exponent; - break; - } - default: - PRINT_ERROR("Unexpected QoS Characteristics type: %d\n", qoS_Characteristics->present); - return false; - break; - } - // NG-RAN Allocation and Retention Priority (M) - ngran_allocation_retention_priority_t *rent_priority = &qos_flow->qos_params.alloc_reten_priority; - E1AP_NGRANAllocationAndRetentionPriority_t *aRP = &qosParams->nGRANallocationRetentionPriority; - rent_priority->priority_level = aRP->priorityLevel; - rent_priority->preemption_capability = aRP->pre_emptionCapability; - rent_priority->preemption_vulnerability = aRP->pre_emptionVulnerability; + CHECK_E1AP_DEC(e1_decode_qos_flow_to_setup(drb->qosFlows + k, qos2SetupList->list.array[k])); } } return true; } +/** @brief Deep copy DRB To Setup List Item (3GPP TS 38.463 9.3.3.2) */ +static DRB_nGRAN_to_setup_t cp_drb_to_setup_item(const DRB_nGRAN_to_setup_t *msg) +{ + DRB_nGRAN_to_setup_t cp = {0}; + cp.id = msg->id; + cp.sdap_config = msg->sdap_config; + cp.pdcp_config = msg->pdcp_config; + cp.numCellGroups = msg->numCellGroups; + // Cell Group List + for (int k = 0; k < msg->numCellGroups; k++) { + cp.cellGroupList[k] = msg->cellGroupList[k]; + } + cp.numQosFlow2Setup = msg->numQosFlow2Setup; + // QoS Flow To Setup List + for (int k = 0; k < msg->numQosFlow2Setup; k++) { + cp.qosFlows[k].qfi = msg->qosFlows[k].qfi; + cp.qosFlows[k].qos_params = msg->qosFlows[k].qos_params; + } + return cp; +} + +/** @brief Free PDU Session Resource To Setup List item (3GPP TS 38.463 9.3.3.2) */ +void free_pdu_session_to_setup_item(const pdu_session_to_setup_t *msg) +{ + free(msg->dlAggregateMaxBitRate); + free(msg->inactivityTimer); + for (int i = 0; i < msg->numDRB2Setup; i++) + free_drb_to_setup_item(&msg->DRBnGRanList[i]); +} + /** * @brief Equality check for PDU session item to modify/setup */ @@ -345,55 +452,20 @@ static bool eq_pdu_session_item(const pdu_session_to_setup_t *a, const pdu_sessi _E1_EQ_CHECK_LONG(a->sessionType, b->sessionType); _E1_EQ_CHECK_INT(a->UP_TL_information.tlAddress, b->UP_TL_information.tlAddress); _E1_EQ_CHECK_INT(a->UP_TL_information.teId, b->UP_TL_information.teId); - _E1_EQ_CHECK_LONG(a->numDRB2Setup, b->numDRB2Setup); - if (a->numDRB2Setup != b->numDRB2Setup) - return false; + _E1_EQ_CHECK_INT(a->numDRB2Setup, b->numDRB2Setup); for (int i = 0; i < a->numDRB2Setup; i++) if (!eq_drb_to_setup(&a->DRBnGRanList[i], &b->DRBnGRanList[i])) return false; - _E1_EQ_CHECK_LONG(a->numDRB2Modify, b->numDRB2Modify); - if (a->numDRB2Modify != b->numDRB2Modify) - return false; - for (int i = 0; i < a->numDRB2Modify; i++) - if (!eq_drb_to_mod(&a->DRBnGRanModList[i], &b->DRBnGRanModList[i])) - return false; + _E1_EQ_CHECK_OPTIONAL_PTR(a, b, inactivityTimer); + if (a->inactivityTimer && b->inactivityTimer) + _E1_EQ_CHECK_INT(a->inactivityTimer, b->inactivityTimer); + _E1_EQ_CHECK_OPTIONAL_IE(a, b, dlAggregateMaxBitRate, _E1_EQ_CHECK_LONG); return true; } -static bool cp_pdu_session_item(pdu_session_to_setup_t *cp, const pdu_session_to_setup_t *msg) -{ - // Copy basic fields - *cp = *msg; - // Copy DRB to Setup List - for (int j = 0; j < msg->numDRB2Setup; j++) { - cp->DRBnGRanList[j].id = msg->DRBnGRanList[j].id; - cp->DRBnGRanList[j].sdap_config = msg->DRBnGRanList[j].sdap_config; - cp->DRBnGRanList[j].pdcp_config = msg->DRBnGRanList[j].pdcp_config; - cp->DRBnGRanList[j].tlAddress = msg->DRBnGRanList[j].tlAddress; - cp->DRBnGRanList[j].teId = msg->DRBnGRanList[j].teId; - cp->DRBnGRanList[j].numDlUpParam = msg->DRBnGRanList[j].numDlUpParam; - // Copy DL UP Params List - for (int k = 0; k < msg->DRBnGRanList[j].numDlUpParam; k++) { - cp->DRBnGRanList[j].DlUpParamList[k] = msg->DRBnGRanList[j].DlUpParamList[k]; - } - cp->DRBnGRanList[j].numCellGroups = msg->DRBnGRanList[j].numCellGroups; - // Copy Cell Group List - for (int k = 0; k < msg->DRBnGRanList[j].numCellGroups; k++) { - cp->DRBnGRanList[j].cellGroupList[k] = msg->DRBnGRanList[j].cellGroupList[k]; - } - cp->DRBnGRanList[j].numQosFlow2Setup = msg->DRBnGRanList[j].numQosFlow2Setup; - // Copy QoS Flow To Setup List - for (int k = 0; k < msg->DRBnGRanList[j].numQosFlow2Setup; k++) { - cp->DRBnGRanList[j].qosFlows[k].qfi = msg->DRBnGRanList[j].qosFlows[k].qfi; - cp->DRBnGRanList[j].qosFlows[k].qos_params = msg->DRBnGRanList[j].qosFlows[k].qos_params; - } - } - // Copy DRB to Modify List - for (int j = 0; j < msg->numDRB2Modify; j++) { - cp->DRBnGRanModList[j] = msg->DRBnGRanModList[j]; - } - return true; -} +/* ==================================== + * E1AP Bearer Context Setup Request + * ==================================== */ /** * @brief E1AP Bearer Context Setup Request encoding (9.2.2 of 3GPP TS 38.463) @@ -421,13 +493,7 @@ E1AP_E1AP_PDU_t *encode_E1_bearer_context_setup_request(const e1ap_bearer_setup_ ieC2->id = E1AP_ProtocolIE_ID_id_SecurityInformation; ieC2->criticality = E1AP_Criticality_reject; ieC2->value.present = E1AP_BearerContextSetupRequestIEs__value_PR_SecurityInformation; - E1AP_SecurityAlgorithm_t *securityAlgorithm = &ieC2->value.choice.SecurityInformation.securityAlgorithm; - E1AP_UPSecuritykey_t *uPSecuritykey = &ieC2->value.choice.SecurityInformation.uPSecuritykey; - securityAlgorithm->cipheringAlgorithm = msg->cipheringAlgorithm; - OCTET_STRING_fromBuf(&uPSecuritykey->encryptionKey, msg->encryptionKey, E1AP_SECURITY_KEY_SIZE); - asn1cCallocOne(securityAlgorithm->integrityProtectionAlgorithm, msg->integrityProtectionAlgorithm); - asn1cCalloc(uPSecuritykey->integrityProtectionKey, protKey); - OCTET_STRING_fromBuf(protKey, msg->integrityProtectionKey, E1AP_SECURITY_KEY_SIZE); + ieC2->value.choice.SecurityInformation = e1_encode_security_info(&msg->secInfo); /* mandatory */ /* c3. UE DL Aggregate Maximum Bit Rate */ asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupRequestIEs_t, ieC3); @@ -479,7 +545,8 @@ E1AP_E1AP_PDU_t *encode_E1_bearer_context_setup_request(const e1ap_bearer_setup_ */ void free_e1ap_context_setup_request(e1ap_bearer_setup_req_t *msg) { - // Do nothing + free(msg->inactivityTimerUE); + free(msg->ueDlMaxIPBitRate); } /** @@ -518,17 +585,7 @@ bool decode_E1_bearer_context_setup_request(const E1AP_E1AP_PDU_t *pdu, e1ap_bea case E1AP_ProtocolIE_ID_id_SecurityInformation: _E1_EQ_CHECK_INT(ie->value.present, E1AP_BearerContextSetupRequestIEs__value_PR_SecurityInformation); - E1AP_SecurityInformation_t *SecurityInformation = &ie->value.choice.SecurityInformation; - E1AP_SecurityAlgorithm_t *securityAlgorithm = &SecurityInformation->securityAlgorithm; - E1AP_EncryptionKey_t *encryptionKey = &SecurityInformation->uPSecuritykey.encryptionKey; - E1AP_IntegrityProtectionKey_t *integrityProtectionKey = SecurityInformation->uPSecuritykey.integrityProtectionKey; - - out->cipheringAlgorithm = securityAlgorithm->cipheringAlgorithm; - memcpy(out->encryptionKey, encryptionKey->buf, encryptionKey->size); - if (securityAlgorithm->integrityProtectionAlgorithm) - out->integrityProtectionAlgorithm = *securityAlgorithm->integrityProtectionAlgorithm; - if (integrityProtectionKey) - memcpy(out->integrityProtectionKey, integrityProtectionKey->buf, integrityProtectionKey->size); + e1_decode_security_info(&out->secInfo, &ie->value.choice.SecurityInformation); break; case E1AP_ProtocolIE_ID_id_UEDLAggregateMaximumBitRate: @@ -583,10 +640,7 @@ bool decode_E1_bearer_context_setup_request(const E1AP_E1AP_PDU_t *pdu, e1ap_bea for (int i = 0; i < pdu2SetupList->list.count; i++) { pdu_session_to_setup_t *pdu_session = out->pduSession + i; E1AP_PDU_Session_Resource_To_Setup_Item_t *pdu2Setup = pdu2SetupList->list.array[i]; - if (!e1_decode_pdu_session_to_setup_item(pdu_session, pdu2Setup)) { - PRINT_ERROR("Failed to decode PDU Session to setup item %d\n", i); - return false; - } + CHECK_E1AP_DEC(e1_decode_pdu_session_to_setup_item(pdu_session, pdu2Setup)); } break; default: @@ -597,19 +651,35 @@ bool decode_E1_bearer_context_setup_request(const E1AP_E1AP_PDU_t *pdu, e1ap_bea return true; } -/** - * @brief Deep copy function for E1 BEARER CONTEXT SETUP REQUEST - */ +/** @brief Deep copy PDU Session Resource To Setup List item (3GPP TS 38.463 9.3.3.2) */ +static pdu_session_to_setup_t cp_pdu_session_item(const pdu_session_to_setup_t *msg) +{ + pdu_session_to_setup_t cp = {0}; + // Copy basic fields + cp = *msg; + // Copy DRB to Setup List + for (int j = 0; j < msg->numDRB2Setup; j++) + cp.DRBnGRanList[j] = cp_drb_to_setup_item(&msg->DRBnGRanList[j]); + // Copy optional IEs + _E1_CP_OPTIONAL_IE(&cp, msg, inactivityTimer); + _E1_CP_OPTIONAL_IE(&cp, msg, dlAggregateMaxBitRate); + return cp; +} + +/** @brief Deep copy function for E1 BEARER CONTEXT SETUP REQUEST */ e1ap_bearer_setup_req_t cp_bearer_context_setup_request(const e1ap_bearer_setup_req_t *msg) { e1ap_bearer_setup_req_t cp = {0}; // Copy basi fields cp = *msg; - strncpy(cp.encryptionKey, msg->encryptionKey, sizeof(cp.encryptionKey)); - strncpy(cp.integrityProtectionKey, msg->integrityProtectionKey, sizeof(cp.integrityProtectionKey)); + strncpy(cp.secInfo.encryptionKey, msg->secInfo.encryptionKey, sizeof(cp.secInfo.encryptionKey)); + strncpy(cp.secInfo.integrityProtectionKey, msg->secInfo.integrityProtectionKey, sizeof(cp.secInfo.integrityProtectionKey)); // Copy PDU Sessions for (int i = 0; i < msg->numPDUSessions; i++) - cp_pdu_session_item(&cp.pduSession[i], &msg->pduSession[i]); + cp.pduSession[i] = cp_pdu_session_item(&msg->pduSession[i]); + // copy optional IEs + _E1_CP_OPTIONAL_IE(&cp, msg, ueDlMaxIPBitRate); + _E1_CP_OPTIONAL_IE(&cp, msg, inactivityTimerUE); return cp; } @@ -620,29 +690,28 @@ bool eq_bearer_context_setup_request(const e1ap_bearer_setup_req_t *a, const e1a { // Primitive data types _E1_EQ_CHECK_INT(a->gNB_cu_cp_ue_id, b->gNB_cu_cp_ue_id); - _E1_EQ_CHECK_LONG(a->cipheringAlgorithm, b->cipheringAlgorithm); - _E1_EQ_CHECK_LONG(a->integrityProtectionAlgorithm, b->integrityProtectionAlgorithm); + _E1_EQ_CHECK_LONG(a->secInfo.cipheringAlgorithm, b->secInfo.cipheringAlgorithm); + _E1_EQ_CHECK_LONG(a->secInfo.integrityProtectionAlgorithm, b->secInfo.integrityProtectionAlgorithm); _E1_EQ_CHECK_LONG(a->ueDlAggMaxBitRate, b->ueDlAggMaxBitRate); _E1_EQ_CHECK_INT(a->numPDUSessions, b->numPDUSessions); - _E1_EQ_CHECK_INT(a->numPDUSessionsMod, b->numPDUSessionsMod); // PLMN _E1_EQ_CHECK_INT(a->servingPLMNid.mcc, b->servingPLMNid.mcc); _E1_EQ_CHECK_INT(a->servingPLMNid.mnc, b->servingPLMNid.mnc); _E1_EQ_CHECK_INT(a->servingPLMNid.mnc_digit_length, b->servingPLMNid.mnc_digit_length); // Security Keys - _E1_EQ_CHECK_STR(a->encryptionKey, b->encryptionKey); - _E1_EQ_CHECK_STR(a->integrityProtectionKey, b->integrityProtectionKey); + _E1_EQ_CHECK_STR(a->secInfo.encryptionKey, b->secInfo.encryptionKey); + _E1_EQ_CHECK_STR(a->secInfo.integrityProtectionKey, b->secInfo.integrityProtectionKey); // PDU Sessions if (a->numPDUSessions != b->numPDUSessions) return false; for (int i = 0; i < a->numPDUSessions; i++) if (!eq_pdu_session_item(&a->pduSession[i], &b->pduSession[i])) return false; - if (a->numPDUSessionsMod != b->numPDUSessionsMod) - return false; - for (int i = 0; i < a->numPDUSessionsMod; i++) - if (!eq_pdu_session_item(&a->pduSessionMod[i], &b->pduSessionMod[i])) - return false; + // Check optional IEs + _E1_EQ_CHECK_OPTIONAL_PTR(a, b, inactivityTimerUE); + if (a->inactivityTimerUE && b->inactivityTimerUE) + _E1_EQ_CHECK_INT(a->inactivityTimerUE, b->inactivityTimerUE); + _E1_EQ_CHECK_OPTIONAL_IE(a, b, ueDlMaxIPBitRate, _E1_EQ_CHECK_LONG); return true; } @@ -664,8 +733,8 @@ static bool e1_decode_pdu_session_setup_item(pdu_session_setup_t *pduSetup, E1AP E1AP_UP_TNL_Information_t *DL_UP_TNL_Info = &in->nG_DL_UP_TNL_Information; if (DL_UP_TNL_Info->choice.gTPTunnel) { _E1_EQ_CHECK_INT(DL_UP_TNL_Info->present, E1AP_UP_TNL_Information_PR_gTPTunnel); - BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&DL_UP_TNL_Info->choice.gTPTunnel->transportLayerAddress, pduSetup->tlAddress); - OCTET_STRING_TO_INT32(&DL_UP_TNL_Info->choice.gTPTunnel->gTP_TEID, pduSetup->teId); + BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&DL_UP_TNL_Info->choice.gTPTunnel->transportLayerAddress, pduSetup->tl_info.tlAddress); + OCTET_STRING_TO_INT32(&DL_UP_TNL_Info->choice.gTPTunnel->gTP_TEID, pduSetup->tl_info.teId); } // DRB Setup List (1..) for (int j = 0; j < in->dRB_Setup_List_NG_RAN.list.count; j++) { @@ -685,8 +754,8 @@ static bool e1_decode_pdu_session_setup_item(pdu_session_setup_t *pduSetup, E1AP E1AP_GTPTunnel_t *gTPTunnel = UL_UP_item->uP_TNL_Information.choice.gTPTunnel; AssertError(gTPTunnel != NULL, return false, "gTPTunnel information in required in UP Transport Layer Information\n"); if (gTPTunnel) { - BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&gTPTunnel->transportLayerAddress, UL_UP_param->tlAddress); - OCTET_STRING_TO_INT32(&gTPTunnel->gTP_TEID, UL_UP_param->teId); + BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&gTPTunnel->transportLayerAddress, UL_UP_param->tl_info.tlAddress); + OCTET_STRING_TO_INT32(&gTPTunnel->gTP_TEID, UL_UP_param->tl_info.teId); } else { } // Cell Group ID (M) @@ -695,7 +764,7 @@ static bool e1_decode_pdu_session_setup_item(pdu_session_setup_t *pduSetup, E1AP // Flow Setup List (M) drbSetup->numQosFlowSetup = drb->flow_Setup_List.list.count; for (int q = 0; q < drb->flow_Setup_List.list.count; q++) { - qos_flow_setup_t *qosflowSetup = &drbSetup->qosFlows[q]; + qos_flow_list_t *qosflowSetup = &drbSetup->qosFlows[q]; E1AP_QoS_Flow_Item_t *in_qosflowSetup = drb->flow_Setup_List.list.array[q]; qosflowSetup->qfi = in_qosflowSetup->qoS_Flow_Identifier; } @@ -713,8 +782,8 @@ static bool e1_encode_pdu_session_setup_item(E1AP_PDU_Session_Resource_Setup_Ite // NG DL UP Transport Layer Information (M) item->nG_DL_UP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; asn1cCalloc(item->nG_DL_UP_TNL_Information.choice.gTPTunnel, gTPTunnel); - TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(in->tlAddress, &gTPTunnel->transportLayerAddress); - INT32_TO_OCTET_STRING(in->teId, &gTPTunnel->gTP_TEID); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(in->tl_info.tlAddress, &gTPTunnel->transportLayerAddress); + INT32_TO_OCTET_STRING(in->tl_info.teId, &gTPTunnel->gTP_TEID); // DRB Setup List (1..) for (const DRB_nGRAN_setup_t *j = in->DRBnGRanList; j < in->DRBnGRanList + in->numDRBSetup; j++) { asn1cSequenceAdd(item->dRB_Setup_List_NG_RAN.list, E1AP_DRB_Setup_Item_NG_RAN_t, ieC3_1_1); @@ -725,11 +794,11 @@ static bool e1_encode_pdu_session_setup_item(E1AP_PDU_Session_Resource_Setup_Ite asn1cSequenceAdd(ieC3_1_1->uL_UP_Transport_Parameters.list, E1AP_UP_Parameters_Item_t, ieC3_1_1_1); ieC3_1_1_1->uP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; asn1cCalloc(ieC3_1_1_1->uP_TNL_Information.choice.gTPTunnel, gTPTunnel); - TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(k->tlAddress, &gTPTunnel->transportLayerAddress); - INT32_TO_OCTET_STRING(k->teId, &gTPTunnel->gTP_TEID); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(k->tl_info.tlAddress, &gTPTunnel->transportLayerAddress); + INT32_TO_OCTET_STRING(k->tl_info.teId, &gTPTunnel->gTP_TEID); } // Flow Setup List(M) - for (const qos_flow_setup_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlowSetup; k++) { + for (const qos_flow_list_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlowSetup; k++) { asn1cSequenceAdd(ieC3_1_1->flow_Setup_List.list, E1AP_QoS_Flow_Item_t, ieC3_1_1_1); ieC3_1_1_1->qoS_Flow_Identifier = k->qfi; } @@ -743,24 +812,7 @@ static bool e1_encode_pdu_session_setup_item(E1AP_PDU_Session_Resource_Setup_Ite // DRB ID (M) ieC3_1_1->dRB_ID = j->id; // Cause (M) - ieC3_1_1->cause.present = j->cause_type; - switch (ieC3_1_1->cause.present) { - case E1AP_Cause_PR_radioNetwork: - ieC3_1_1->cause.choice.radioNetwork = j->cause; - break; - case E1AP_Cause_PR_transport: - ieC3_1_1->cause.choice.transport = j->cause; - break; - case E1AP_Cause_PR_protocol: - ieC3_1_1->cause.choice.protocol = j->cause; - break; - case E1AP_Cause_PR_misc: - ieC3_1_1->cause.choice.misc = j->cause; - break; - default: - PRINT_ERROR("DRB setup failure cause out of expected range\n"); - break; - } + ieC3_1_1->cause = e1_encode_cause_ie(&j->cause); } return true; } @@ -929,8 +981,8 @@ bool eq_bearer_context_setup_response(const e1ap_bearer_setup_resp_t *a, const e const pdu_session_setup_t *ps_a = &a->pduSession[i]; const pdu_session_setup_t *ps_b = &b->pduSession[i]; _E1_EQ_CHECK_LONG(ps_a->id, ps_b->id); - _E1_EQ_CHECK_INT(ps_a->tlAddress, ps_b->tlAddress); - _E1_EQ_CHECK_LONG(ps_a->teId, ps_b->teId); + _E1_EQ_CHECK_INT(ps_a->tl_info.tlAddress, ps_b->tl_info.tlAddress); + _E1_EQ_CHECK_LONG(ps_a->tl_info.teId, ps_b->tl_info.teId); _E1_EQ_CHECK_INT(ps_a->numDRBSetup, ps_b->numDRBSetup); _E1_EQ_CHECK_INT(ps_a->numDRBFailed, ps_b->numDRBFailed); // Check DRB Setup @@ -946,8 +998,8 @@ bool eq_bearer_context_setup_response(const e1ap_bearer_setup_resp_t *a, const e const DRB_nGRAN_failed_t *drbf_a = &ps_a->DRBnGRanFailedList[j]; const DRB_nGRAN_failed_t *drbf_b = &ps_b->DRBnGRanFailedList[j]; _E1_EQ_CHECK_LONG(drbf_a->id, drbf_b->id); - _E1_EQ_CHECK_LONG(drbf_a->cause_type, drbf_b->cause_type); - _E1_EQ_CHECK_LONG(drbf_a->cause, drbf_b->cause); + _E1_EQ_CHECK_LONG(drbf_a->cause.type, drbf_b->cause.type); + _E1_EQ_CHECK_LONG(drbf_a->cause.value, drbf_b->cause.value); } } return true; diff --git a/openair2/E1AP/lib/e1ap_lib_includes.h b/openair2/E1AP/lib/e1ap_lib_includes.h index 443d59e19e..785fd9d09c 100644 --- a/openair2/E1AP/lib/e1ap_lib_includes.h +++ b/openair2/E1AP/lib/e1ap_lib_includes.h @@ -48,6 +48,7 @@ #include "E1AP_System-BearerContextSetupRequest.h" #include "E1AP_MaximumIPdatarate.h" #include "E1AP_PDU-Session-Type.h" +#include "E1AP_Data-Forwarding-Information.h" // E1 Bearer Context Setup Response #include "E1AP_PDU-Session-Resource-Setup-Item.h" #include "E1AP_DRB-Setup-Item-NG-RAN.h" diff --git a/openair2/E1AP/tests/e1ap_lib_test.c b/openair2/E1AP/tests/e1ap_lib_test.c index 9a53e8074d..bc670486cd 100644 --- a/openair2/E1AP/tests/e1ap_lib_test.c +++ b/openair2/E1AP/tests/e1ap_lib_test.c @@ -70,6 +70,14 @@ static void e1ap_msg_free(E1AP_E1AP_PDU_t *pdu) ASN_STRUCT_FREE(asn_DEF_E1AP_E1AP_PDU, pdu); } +static UP_TL_information_t create_up_tl_info(void) +{ + UP_TL_information_t tl_info; + tl_info.tlAddress = htonl(0xC0A90001); // 192.169.0.1 + tl_info.teId = 0x2345; + return tl_info; +} + /** * @brief Test E1AP Bearer Context Setup Request encoding/decoding */ @@ -107,8 +115,8 @@ static void test_bearer_context_setup_request(void) // Step 1: Initialize the E1AP Bearer Context Setup Request e1ap_bearer_setup_req_t orig = { .gNB_cu_cp_ue_id = 1234, - .cipheringAlgorithm = 0x01, - .integrityProtectionAlgorithm = 0x01, + .secInfo.cipheringAlgorithm = 0x01, + .secInfo.integrityProtectionAlgorithm = 0x01, .ueDlAggMaxBitRate = 1000000000, .bearerContextStatus = 0, .servingPLMNid.mcc = 001, @@ -121,8 +129,7 @@ static void test_bearer_context_setup_request(void) .pduSession[0].nssai.sst = 0x01, .pduSession[0].securityIndication = security, .pduSession[0].numDRB2Setup = 1, - .pduSession[0].UP_TL_information.tlAddress = 167772161, - .pduSession[0].UP_TL_information.teId = 0x12345, + .pduSession[0].UP_TL_information = create_up_tl_info(), .pduSession[0].DRBnGRanList[0].id = 1, .pduSession[0].DRBnGRanList[0].sdap_config = sdap, .pduSession[0].DRBnGRanList[0].pdcp_config = pdcp, @@ -132,8 +139,8 @@ static void test_bearer_context_setup_request(void) .pduSession[0].DRBnGRanList[0].numQosFlow2Setup = 1, .pduSession[0].DRBnGRanList[0].qosFlows[0] = qos, }; - memset(orig.encryptionKey, 0xAB, sizeof(orig.encryptionKey)); - memset(orig.integrityProtectionKey, 0xCD, sizeof(orig.integrityProtectionKey)); + memset(orig.secInfo.encryptionKey, 0xAB, sizeof(orig.secInfo.encryptionKey)); + memset(orig.secInfo.integrityProtectionKey, 0xCD, sizeof(orig.secInfo.integrityProtectionKey)); // E1AP encode the original message E1AP_E1AP_PDU_t *enc = encode_E1_bearer_context_setup_request(&orig); // E1AP decode the encoded message @@ -171,8 +178,7 @@ static void test_bearer_context_setup_response(void) .gNB_cu_up_ue_id = 5678, .numPDUSessions = 1, .pduSession[0].id = 1, - .pduSession[0].tlAddress = 167772161, - .pduSession[0].teId = 0x12345, + .pduSession[0].tl_info = create_up_tl_info(), .pduSession[0].numDRBSetup = 1, .pduSession[0].numDRBFailed = 0, .pduSession[0].DRBnGRanList[0].id = 1, @@ -180,8 +186,7 @@ static void test_bearer_context_setup_response(void) .pduSession[0].DRBnGRanList[0].numQosFlowSetup = 1, .pduSession[0].DRBnGRanList[0].qosFlows[0].qfi = 1, .pduSession[0].DRBnGRanList[0].UpParamList[0].cell_group_id = MCG, - .pduSession[0].DRBnGRanList[0].UpParamList[0].teId = 0x34345, - .pduSession[0].DRBnGRanList[0].UpParamList[0].tlAddress = 167772161, + .pduSession[0].DRBnGRanList[0].UpParamList[0].tl_info = create_up_tl_info(), }; // E1AP encode the original message E1AP_E1AP_PDU_t *enc = encode_E1_bearer_context_setup_response(&orig); diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index 380ae1f60a..ed39dbeff0 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -168,7 +168,6 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) const pdu_session_to_setup_t *req_pdu = req->pduSession + i; resp_pdu->id = req_pdu->sessionId; - AssertFatal(req_pdu->numDRB2Modify == 0, "DRB modification not implemented\n"); AssertFatal(req_pdu->numDRB2Setup == 1, "can only handle one DRB per PDU session\n"); resp_pdu->numDRBSetup = req_pdu->numDRB2Setup; const DRB_nGRAN_to_setup_t *req_drb = &req_pdu->DRBnGRanList[0]; @@ -178,7 +177,7 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) resp_drb->numQosFlowSetup = req_drb->numQosFlow2Setup; for (int k = 0; k < resp_drb->numQosFlowSetup; k++) { const qos_flow_to_setup_t *qosflow2Setup = &req_drb->qosFlows[k]; - qos_flow_setup_t *qosflowSetup = &resp_drb->qosFlows[k]; + qos_flow_list_t *qosflowSetup = &resp_drb->qosFlows[k]; qosflowSetup->qfi = qosflow2Setup->qfi; } @@ -197,17 +196,17 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) &resp_n3); AssertFatal(ret >= 0, "Unable to create GTP Tunnel for NG-U\n"); AssertFatal(resp_n3.num_tunnels == req_pdu->numDRB2Setup, "could not create all tunnels\n"); - resp_pdu->teId = resp_n3.gnb_NGu_teid[0]; - memcpy(&resp_pdu->tlAddress, &resp_n3.gnb_addr.buffer, 4); + resp_pdu->tl_info.teId = resp_n3.gnb_NGu_teid[0]; + memcpy(&resp_pdu->tl_info.tlAddress, &resp_n3.gnb_addr.buffer, 4); // create PDCP bearers. This will also create SDAP bearers NR_DRB_ToAddModList_t DRB_configList = {0}; fill_DRB_configList_e1(&DRB_configList, req_pdu); nr_pdcp_entity_security_keys_and_algos_t security_parameters; - security_parameters.ciphering_algorithm = req->cipheringAlgorithm; - security_parameters.integrity_algorithm = req->integrityProtectionAlgorithm; - memcpy(security_parameters.ciphering_key, req->encryptionKey, NR_K_KEY_SIZE); - memcpy(security_parameters.integrity_key, req->integrityProtectionKey, NR_K_KEY_SIZE); + security_parameters.ciphering_algorithm = req->secInfo.cipheringAlgorithm; + security_parameters.integrity_algorithm = req->secInfo.integrityProtectionAlgorithm; + memcpy(security_parameters.ciphering_key, req->secInfo.encryptionKey, NR_K_KEY_SIZE); + memcpy(security_parameters.integrity_key, req->secInfo.integrityProtectionKey, NR_K_KEY_SIZE); nr_pdcp_add_drbs(true, // set this to notify PDCP that his not UE cu_up_ue_id, &DRB_configList, @@ -230,8 +229,8 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) &resp_f1); resp_drb->numUpParam = 1; AssertFatal(ret >= 0, "Unable to create GTP Tunnel for F1-U\n"); - memcpy(&resp_drb->UpParamList[0].tlAddress, &resp_f1.gnb_addr.buffer, 4); - resp_drb->UpParamList[0].teId = resp_f1.gnb_NGu_teid[0]; + memcpy(&resp_drb->UpParamList[0].tl_info.tlAddress, &resp_f1.gnb_addr.buffer, 4); + resp_drb->UpParamList[0].tl_info.teId = resp_f1.gnb_NGu_teid[0]; } // We assume all DRBs to setup have been setup successfully, so we always @@ -273,12 +272,14 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) DRB_nGRAN_modified_t *modified = &modif.pduSessionMod[i].DRBnGRanModList[j]; modified->id = to_modif->id; - if (to_modif->pdcp_config.pDCP_Reestablishment) { + if (to_modif->pdcp_config && to_modif->pdcp_config->pDCP_Reestablishment) { nr_pdcp_entity_security_keys_and_algos_t security_parameters; - security_parameters.ciphering_algorithm = req->cipheringAlgorithm; - security_parameters.integrity_algorithm = req->integrityProtectionAlgorithm; - memcpy(security_parameters.ciphering_key, req->encryptionKey, NR_K_KEY_SIZE); - memcpy(security_parameters.integrity_key, req->integrityProtectionKey, NR_K_KEY_SIZE); + if (req->secInfo) { + security_parameters.ciphering_algorithm = req->secInfo->cipheringAlgorithm; + security_parameters.integrity_algorithm = req->secInfo->integrityProtectionAlgorithm; + memcpy(security_parameters.ciphering_key, req->secInfo->encryptionKey, NR_K_KEY_SIZE); + memcpy(security_parameters.integrity_key, req->secInfo->integrityProtectionKey, NR_K_KEY_SIZE); + } nr_pdcp_reestablishment(req->gNB_cu_up_ue_id, to_modif->id, false, @@ -291,8 +292,8 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) /* Loop through DL UP Transport Layer params list * and update GTP tunnel outgoing addr and TEID */ for (int k = 0; k < to_modif->numDlUpParam; k++) { - in_addr_t addr = to_modif->DlUpParamList[k].tlAddress; - GtpuUpdateTunnelOutgoingAddressAndTeid(f1inst, req->gNB_cu_cp_ue_id, to_modif->id, addr, to_modif->DlUpParamList[k].teId); + in_addr_t addr = to_modif->DlUpParamList[k].tl_info.tlAddress; + GtpuUpdateTunnelOutgoingAddressAndTeid(f1inst, req->gNB_cu_cp_ue_id, to_modif->id, addr, to_modif->DlUpParamList[k].tl_info.teId); } } } diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.h b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.h index 7290043371..5d6860f2a7 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.h +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.h @@ -27,9 +27,10 @@ void nr_pdcp_e1_if_init(bool uses_e1); struct e1ap_bearer_setup_req_s; +struct e1ap_bearer_mod_req_s; struct e1ap_bearer_release_cmd_s; void e1_bearer_context_setup(const struct e1ap_bearer_setup_req_s *req); -void e1_bearer_context_modif(const struct e1ap_bearer_setup_req_s *req); +void e1_bearer_context_modif(const struct e1ap_bearer_mod_req_s *req); void e1_bearer_release_cmd(const struct e1ap_bearer_release_cmd_s *cmd); #endif /* CUCP_CUUP_HANDLER_H */ diff --git a/openair2/RRC/NR/cucp_cuup_if.h b/openair2/RRC/NR/cucp_cuup_if.h index 86b3a2435e..418e1f362e 100644 --- a/openair2/RRC/NR/cucp_cuup_if.h +++ b/openair2/RRC/NR/cucp_cuup_if.h @@ -29,9 +29,11 @@ #include "common/platform_types.h" struct e1ap_bearer_setup_req_s; +struct e1ap_bearer_mod_req_s; struct e1ap_bearer_setup_resp_s; struct e1ap_bearer_release_cmd_s; typedef void (*cucp_cuup_bearer_context_setup_func_t)(sctp_assoc_t assoc_id, const struct e1ap_bearer_setup_req_s *req); +typedef void (*cucp_cuup_bearer_context_mod_func_t)(sctp_assoc_t assoc_id, const struct e1ap_bearer_mod_req_s *req); typedef void (*cucp_cuup_bearer_context_release_func_t)(sctp_assoc_t assoc_id, const struct e1ap_bearer_release_cmd_s *cmd); struct gNB_RRC_INST_s; diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 953f723853..0861ebedb2 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -345,7 +345,7 @@ typedef struct nr_mac_rrc_dl_if_s { typedef struct cucp_cuup_if_s { cucp_cuup_bearer_context_setup_func_t bearer_context_setup; - cucp_cuup_bearer_context_setup_func_t bearer_context_mod; + cucp_cuup_bearer_context_mod_func_t bearer_context_mod; cucp_cuup_bearer_context_release_func_t bearer_context_release; } cucp_cuup_if_t; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index b4d33b16f0..6cfb0047af 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -740,6 +740,14 @@ void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc, nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, buffer, size); } +static void fill_security_info(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, security_information_t *secInfo) +{ + secInfo->cipheringAlgorithm = rrc->security.do_drb_ciphering ? UE->ciphering_algorithm : 0; + secInfo->integrityProtectionAlgorithm = rrc->security.do_drb_integrity ? UE->integrity_algorithm : 0; + nr_derive_key(UP_ENC_ALG, secInfo->cipheringAlgorithm, UE->kgnb, (uint8_t *)secInfo->encryptionKey); + nr_derive_key(UP_INT_ALG, secInfo->integrityProtectionAlgorithm, UE->kgnb, (uint8_t *)secInfo->integrityProtectionKey); +} + /* \brief find existing PDU session inside E1AP Bearer Modif message, or * point to new one. * \param bearer_modif E1AP Bearer Modification Message @@ -797,21 +805,20 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify]; drb_e1->id = drb_id; drb_e1->numDlUpParam = 1; - memcpy(&drb_e1->DlUpParamList[0].tlAddress, &drb->du_tunnel_config.addr.buffer, sizeof(uint8_t) * 4); - drb_e1->DlUpParamList[0].teId = drb->du_tunnel_config.teid; + memcpy(&drb_e1->DlUpParamList[0].tl_info.tlAddress, &drb->du_tunnel_config.addr.buffer, sizeof(uint8_t) * 4); + drb_e1->DlUpParamList[0].tl_info.teId = drb->du_tunnel_config.teid; /* PDCP configuration */ - bearer_context_pdcp_config_t *pdcp_config = &drb_e1->pdcp_config; + if (!drb_e1->pdcp_config) + drb_e1->pdcp_config = malloc_or_fail(sizeof(*drb_e1->pdcp_config)); + bearer_context_pdcp_config_t *pdcp_config = drb_e1->pdcp_config; set_bearer_context_pdcp_config(pdcp_config, drb, rrc->configuration.um_on_default_drb); pdcp_config->pDCP_Reestablishment = true; /* increase DRB to modify counter */ pdu_e1->numDRB2Modify += 1; } - - req.cipheringAlgorithm = rrc->security.do_drb_ciphering ? ue_p->ciphering_algorithm : 0; - req.integrityProtectionAlgorithm = rrc->security.do_drb_integrity ? ue_p->integrity_algorithm : 0; - nr_derive_key(UP_ENC_ALG, req.cipheringAlgorithm, ue_p->kgnb, (uint8_t *)req.encryptionKey); - nr_derive_key(UP_INT_ALG, req.integrityProtectionAlgorithm, ue_p->kgnb, (uint8_t *)req.integrityProtectionKey); - + // Always send security information + req.secInfo = malloc_or_fail(sizeof(*req.secInfo)); + fill_security_info(rrc, ue_p, req.secInfo); /* Send E1 Bearer Context Modification Request (3GPP TS 38.463) */ sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, ue_p); rrc->cucp_cuup.bearer_context_mod(assoc_id, &req); @@ -1859,12 +1866,12 @@ void rrc_gNB_process_dc_overall_timeout(const module_id_t gnb_mod_idP, x2ap_ENDC /* \brief fill E1 bearer modification's DRB from F1 DRB * \param drb_e1 pointer to a DRB inside an E1 bearer modification message * \param drb_f1 pointer to a DRB inside an F1 UE Ctxt modification Response */ -static void fill_e1_bearer_modif(DRB_nGRAN_to_setup_t *drb_e1, const f1ap_drb_to_be_setup_t *drb_f1) +static void fill_e1_bearer_modif(DRB_nGRAN_to_mod_t *drb_e1, const f1ap_drb_to_be_setup_t *drb_f1) { drb_e1->id = drb_f1->drb_id; drb_e1->numDlUpParam = drb_f1->up_dl_tnl_length; - drb_e1->DlUpParamList[0].tlAddress = drb_f1->up_dl_tnl[0].tl_address; - drb_e1->DlUpParamList[0].teId = drb_f1->up_dl_tnl[0].teid; + drb_e1->DlUpParamList[0].tl_info.tlAddress = drb_f1->up_dl_tnl[0].tl_address; + drb_e1->DlUpParamList[0].tl_info.teId = drb_f1->up_dl_tnl[0].teid; } /** @@ -1896,8 +1903,8 @@ static void store_du_f1u_tunnel(const f1ap_drb_to_be_setup_t *drbs, int n, gNB_R */ static void f1u_ul_gtp_update(f1u_tunnel_t *f1u, const up_params_t *p) { - f1u->teid = p->teId; - memcpy(&f1u->addr.buffer, &p->tlAddress, sizeof(uint8_t) * 4); + f1u->teid = p->tl_info.teId; + memcpy(&f1u->addr.buffer, &p->tl_info.tlAddress, sizeof(uint8_t) * 4); f1u->addr.length = sizeof(in_addr_t); } @@ -1923,10 +1930,10 @@ static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f LOG_E(RRC, "UE %d: UE Context Modif Response: no PDU session for DRB ID %ld\n", UE->rrc_ue_id, drb_f1->drb_id); continue; } - pdu_session_to_setup_t *pdu_e1 = find_or_next_pdu_session(&req, pdu_ue->param.pdusession_id); + pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, pdu_ue->param.pdusession_id); DevAssert(pdu_e1 != NULL); pdu_e1->sessionId = pdu_ue->param.pdusession_id; - DRB_nGRAN_to_setup_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify]; + DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify]; /* Fill E1 bearer context modification */ fill_e1_bearer_modif(drb_e1, drb_f1); pdu_e1->numDRB2Modify += 1; @@ -1934,10 +1941,9 @@ static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f DevAssert(req.numPDUSessionsMod > 0); DevAssert(req.numPDUSessions == 0); - req.cipheringAlgorithm = rrc->security.do_drb_ciphering ? UE->ciphering_algorithm : 0; - req.integrityProtectionAlgorithm = rrc->security.do_drb_integrity ? UE->integrity_algorithm : 0; - nr_derive_key(UP_ENC_ALG, req.cipheringAlgorithm, UE->kgnb, (uint8_t *)req.encryptionKey); - nr_derive_key(UP_INT_ALG, req.integrityProtectionAlgorithm, UE->kgnb, (uint8_t *)req.integrityProtectionKey); + // Always send security information + req.secInfo = malloc_or_fail(sizeof(*req.secInfo)); + fill_security_info(rrc, UE, req.secInfo); // send the E1 bearer modification request message to update F1-U tunnel info sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE); @@ -2275,9 +2281,9 @@ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc, f1ap_drb_to_be_setup_t *drb = &drbs[nb_drb]; drb->drb_id = pduSession[p].DRBnGRanList[i].id; drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM; - drb->up_ul_tnl[0].tl_address = drb_config->UpParamList[0].tlAddress; + drb->up_ul_tnl[0].tl_address = drb_config->UpParamList[0].tl_info.tlAddress; drb->up_ul_tnl[0].port = rrc->eth_params_s.my_portd; - drb->up_ul_tnl[0].teid = drb_config->UpParamList[0].teId; + drb->up_ul_tnl[0].teid = drb_config->UpParamList[0].tl_info.teId; drb->up_ul_tnl_length = 1; /* pass QoS info to MAC */ @@ -2328,8 +2334,8 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp LOG_W(RRC, "E1: received setup for PDU session %ld, but has not been requested\n", e1_pdu->id); continue; } - rrc_pdu->param.gNB_teid_N3 = e1_pdu->teId; - memcpy(&rrc_pdu->param.gNB_addr_N3.buffer, &e1_pdu->tlAddress, sizeof(uint8_t) * 4); + rrc_pdu->param.gNB_teid_N3 = e1_pdu->tl_info.teId; + memcpy(&rrc_pdu->param.gNB_addr_N3.buffer, &e1_pdu->tl_info.tlAddress, sizeof(uint8_t) * 4); rrc_pdu->param.gNB_addr_N3.length = sizeof(in_addr_t); // save the tunnel address for the DRBs diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 08f83ea5c8..7f17baea4a 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -355,10 +355,11 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession session->nssai = sessions[i].nssai; decodePDUSessionResourceSetup(session); bearer_req.gNB_cu_cp_ue_id = UE->rrc_ue_id; - bearer_req.cipheringAlgorithm = UE->ciphering_algorithm; - bearer_req.integrityProtectionAlgorithm = UE->integrity_algorithm; - nr_derive_key(UP_ENC_ALG, UE->ciphering_algorithm, UE->kgnb, (uint8_t *)bearer_req.encryptionKey); - nr_derive_key(UP_INT_ALG, UE->integrity_algorithm, UE->kgnb, (uint8_t *)bearer_req.integrityProtectionKey); + security_information_t *secInfo = &bearer_req.secInfo; + secInfo->cipheringAlgorithm = UE->ciphering_algorithm; + secInfo->integrityProtectionAlgorithm = UE->integrity_algorithm; + nr_derive_key(UP_ENC_ALG, UE->ciphering_algorithm, UE->kgnb, (uint8_t *)secInfo->encryptionKey); + nr_derive_key(UP_INT_ALG, UE->integrity_algorithm, UE->kgnb, (uint8_t *)secInfo->integrityProtectionKey); bearer_req.ueDlAggMaxBitRate = ueAggMaxBitRateDownlink; pdu_session_to_setup_t *pdu = bearer_req.pduSession + bearer_req.numPDUSessions; bearer_req.numPDUSessions++; From 1ef24796f91be77930b6fe6ad05a7aa0a3f1f9c0 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 4 Feb 2025 12:03:01 +0100 Subject: [PATCH 32/48] Add compile definitions E1AP_LOG_ERRORS * this is necessary to enable the error logs in the E1 libs * also, updated the define name in the E1AP lib --- openair2/E1AP/CMakeLists.txt | 4 ++++ openair2/E1AP/lib/e1ap_lib_common.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/openair2/E1AP/CMakeLists.txt b/openair2/E1AP/CMakeLists.txt index 5849a32fe5..eb3bd0f546 100644 --- a/openair2/E1AP/CMakeLists.txt +++ b/openair2/E1AP/CMakeLists.txt @@ -10,3 +10,7 @@ target_include_directories(e1ap PUBLIC ${CMAKE_CURRENT_DIR}) if(ENABLE_TESTS) add_subdirectory(tests) endif() + +if(ENABLE_TESTS) + target_compile_definitions(e1ap PRIVATE E1AP_LOG_ERRORS) +endif() diff --git a/openair2/E1AP/lib/e1ap_lib_common.h b/openair2/E1AP/lib/e1ap_lib_common.h index 39e3d64448..6fda2cd8ca 100644 --- a/openair2/E1AP/lib/e1ap_lib_common.h +++ b/openair2/E1AP/lib/e1ap_lib_common.h @@ -28,7 +28,7 @@ #include "openair3/UTILS/conversions.h" #include "e1ap_messages_types.h" -#ifdef ENABLE_TESTS +#ifdef E1AP_LOG_ERRORS #define PRINT_ERROR(...) fprintf(stderr, ##__VA_ARGS__); #else #define PRINT_ERROR(...) // Do nothing From cd7218c9765ec8275e28e274763a65984f4c9ba4 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 4 Feb 2025 12:21:26 +0100 Subject: [PATCH 33/48] Improvements to E1 Bearer Context Setup Request/Response * Add enc/dec/cp/eq sub-functions to E1 library and adopt in stack * eq_drb_to_mod * encode_dl_up_parameters * decode_dl_up_parameters * e1_decode_snssai * e1_encode_snssai * eq_security_ind * Check result of enc/dec functions in Bearer Context Setup enc/dec libs --- .../E1AP/lib/e1ap_bearer_context_management.c | 154 +++++++++++------- 1 file changed, 96 insertions(+), 58 deletions(-) diff --git a/openair2/E1AP/lib/e1ap_bearer_context_management.c b/openair2/E1AP/lib/e1ap_bearer_context_management.c index d73a3bddd7..e58fcbf145 100644 --- a/openair2/E1AP/lib/e1ap_bearer_context_management.c +++ b/openair2/E1AP/lib/e1ap_bearer_context_management.c @@ -293,6 +293,62 @@ static bool eq_qos_flow(const qos_flow_to_setup_t *a, const qos_flow_to_setup_t return true; } +/** @brief S-NSSAI Encoding (3GPP TS 38.463 9.3.1.9) */ +static E1AP_SNSSAI_t e1_encode_snssai(const nssai_t *in) +{ + E1AP_SNSSAI_t out = {0}; + INT8_TO_OCTET_STRING(in->sst, &out.sST); + if (in->sd != 0xffffff) { + out.sD = malloc_or_fail(sizeof(*out.sD)); + INT24_TO_OCTET_STRING(in->sd, out.sD); + } + return out; +} + +/** @brief S-NSSAI Decoding (3GPP TS 38.463 9.3.1.9) */ +static bool e1_decode_snssai(e1ap_nssai_t *out, const E1AP_SNSSAI_t *in) +{ + // SST (M) + OCTET_STRING_TO_INT8(&in->sST, out->sst); + out->sd = 0xffffff; + // SD (O) + if (in->sD != NULL) + OCTET_STRING_TO_INT24(in->sD, out->sd); + return true; +} + +/** + * @brief Decode E1AP_UP_Parameters_t + */ +static int decode_dl_up_parameters(up_params_t *dl_up_param, const E1AP_UP_Parameters_t *dl_up_paramList) +{ + for (int k = 0; k < dl_up_paramList->list.count; k++) { + dl_up_param = dl_up_param + k; + E1AP_UP_Parameters_Item_t *dl_up_param_in = dl_up_paramList->list.array[k]; + // GTP tunnel + if (!e1_decode_up_tnl_info(&dl_up_param->tl_info, &dl_up_param_in->uP_TNL_Information)) { + PRINT_ERROR("GTP tunnel decoding failed\n"); + return -1; + } + // Cell group ID + dl_up_param->cell_group_id = dl_up_param_in->cell_Group_ID; + } + return dl_up_paramList->list.count; +} + +/** + * @brief Encode E1AP_UP_Parameters_t IE + */ +static E1AP_UP_Parameters_t encode_dl_up_parameters(const int numUpParam, const up_params_t *in) +{ + E1AP_UP_Parameters_t out = {0}; + for (const up_params_t *k = in; k < in + numUpParam; k++) { + asn1cSequenceAdd(out.list, E1AP_UP_Parameters_Item_t, ieC3_1_1_1); + ieC3_1_1_1->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info); + } + return out; +} + /** * @brief Equality check for DRB_nGRAN_to_setup_t */ @@ -321,14 +377,12 @@ static void free_drb_to_setup_item(const DRB_nGRAN_to_setup_t *msg) /** @brief Encode PDU Session Resource To Setup List Item (3GPP TS 38.463 9.3.3.2) */ static bool e1_encode_pdu_session_to_setup_item(E1AP_PDU_Session_Resource_To_Setup_Item_t *item, const pdu_session_to_setup_t *in) { + // PDU Session ID (M) item->pDU_Session_ID = in->sessionId; + // PDU Session Type (M) item->pDU_Session_Type = in->sessionType; - // SNSSAI - INT8_TO_OCTET_STRING(in->nssai.sst, &item->sNSSAI.sST); - if (in->nssai.sd != 0xffffff) { - item->sNSSAI.sD = malloc_or_fail(sizeof(*item->sNSSAI.sD)); - INT24_TO_OCTET_STRING(in->nssai.sd, item->sNSSAI.sD); - } + // S-NSSAI (M) + item->sNSSAI = e1_encode_snssai(&in->nssai); // Security Indication (M) item->securityIndication = e1_encode_security_indication(&in->securityIndication); // NG UL UP Transport Layer Information (M) @@ -336,6 +390,7 @@ static bool e1_encode_pdu_session_to_setup_item(E1AP_PDU_Session_Resource_To_Set // DRB To Setup List (M) for (const DRB_nGRAN_to_setup_t *j = in->DRBnGRanList; j < in->DRBnGRanList + in->numDRB2Setup; j++) { asn1cSequenceAdd(item->dRB_To_Setup_List_NG_RAN.list, E1AP_DRB_To_Setup_Item_NG_RAN_t, ieC6_1_1); + // DRB ID (M) ieC6_1_1->dRB_ID = j->id; // SDAP Configuration (M) ieC6_1_1->sDAP_Configuration = e1_encode_sdap_config(&j->sdap_config); @@ -346,7 +401,7 @@ static bool e1_encode_pdu_session_to_setup_item(E1AP_PDU_Session_Resource_To_Set asn1cSequenceAdd(ieC6_1_1->cell_Group_Information.list, E1AP_Cell_Group_Information_Item_t, ieC6_1_1_1); ieC6_1_1_1->cell_Group_ID = *k; } - // QoS Flows + // QoS Flows Information To Be Setup (M) for (const qos_flow_to_setup_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlow2Setup; k++) { asn1cSequenceAdd(ieC6_1_1->qos_flow_Information_To_Be_Setup.list, E1AP_QoS_Flow_QoS_Parameter_Item_t, ieC6_1_1_2); *ieC6_1_1_2 = e1_encode_qos_flow_to_setup(k); @@ -362,13 +417,8 @@ static bool e1_decode_pdu_session_to_setup_item(pdu_session_to_setup_t *out, E1A out->sessionId = item->pDU_Session_ID; // PDU Session Type (M) out->sessionType = item->pDU_Session_Type; - /* S-NSSAI (M) */ - // SST (M) - OCTET_STRING_TO_INT8(&item->sNSSAI.sST, out->nssai.sst); - out->nssai.sd = 0xffffff; - // SD (O) - if (item->sNSSAI.sD != NULL) - OCTET_STRING_TO_INT24(item->sNSSAI.sD, out->nssai.sd); + // S-NSSAI (M) + CHECK_E1AP_DEC(e1_decode_snssai(&out->nssai, &item->sNSSAI)); // Security Indication (M) CHECK_E1AP_DEC(e1_decode_security_indication(&out->securityIndication, &item->securityIndication)); /* NG UL UP Transport Layer Information (M) (9.3.2.1 of 3GPP TS 38.463) */ @@ -443,6 +493,14 @@ void free_pdu_session_to_setup_item(const pdu_session_to_setup_t *msg) free_drb_to_setup_item(&msg->DRBnGRanList[i]); } +/** @brief Equality check for UP Transport Layer Information (3GPP TS 38.463 9.3.2.1)*/ +static bool eq_up_tl_info(const UP_TL_information_t *a, const UP_TL_information_t *b) +{ + _E1_EQ_CHECK_INT(a->tlAddress, b->tlAddress); + _E1_EQ_CHECK_INT(a->teId, b->teId); + return true; +} + /** * @brief Equality check for PDU session item to modify/setup */ @@ -450,8 +508,7 @@ static bool eq_pdu_session_item(const pdu_session_to_setup_t *a, const pdu_sessi { _E1_EQ_CHECK_LONG(a->sessionId, b->sessionId); _E1_EQ_CHECK_LONG(a->sessionType, b->sessionType); - _E1_EQ_CHECK_INT(a->UP_TL_information.tlAddress, b->UP_TL_information.tlAddress); - _E1_EQ_CHECK_INT(a->UP_TL_information.teId, b->UP_TL_information.teId); + eq_up_tl_info(&a->UP_TL_information, &b->UP_TL_information); _E1_EQ_CHECK_INT(a->numDRB2Setup, b->numDRB2Setup); for (int i = 0; i < a->numDRB2Setup; i++) if (!eq_drb_to_setup(&a->DRBnGRanList[i], &b->DRBnGRanList[i])) @@ -516,8 +573,7 @@ E1AP_E1AP_PDU_t *encode_E1_bearer_context_setup_request(const e1ap_bearer_setup_ ieC5->criticality = E1AP_Criticality_reject; ieC5->value.present = E1AP_BearerContextSetupRequestIEs__value_PR_ActivityNotificationLevel; ieC5->value.choice.ActivityNotificationLevel = E1AP_ActivityNotificationLevel_pdu_session; // TODO: Remove hard coding - /* mandatory */ - /* */ + // System (M) asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupRequestIEs_t, ieC6); ieC6->id = E1AP_ProtocolIE_ID_id_System_BearerContextSetupRequest; ieC6->criticality = E1AP_Criticality_reject; @@ -535,7 +591,7 @@ E1AP_E1AP_PDU_t *encode_E1_bearer_context_setup_request(const e1ap_bearer_setup_ for (int i = 0; i < msg->numPDUSessions; i++) { const pdu_session_to_setup_t *pdu_session = &msg->pduSession[i]; asn1cSequenceAdd(pdu2Setup->list, E1AP_PDU_Session_Resource_To_Setup_Item_t, ieC6_1); - e1_encode_pdu_session_to_setup_item(ieC6_1, pdu_session); + CHECK_E1AP_DEC(e1_encode_pdu_session_to_setup_item(ieC6_1, pdu_session)); } return pdu; } @@ -585,7 +641,7 @@ bool decode_E1_bearer_context_setup_request(const E1AP_E1AP_PDU_t *pdu, e1ap_bea case E1AP_ProtocolIE_ID_id_SecurityInformation: _E1_EQ_CHECK_INT(ie->value.present, E1AP_BearerContextSetupRequestIEs__value_PR_SecurityInformation); - e1_decode_security_info(&out->secInfo, &ie->value.choice.SecurityInformation); + CHECK_E1AP_DEC(e1_decode_security_info(&out->secInfo, &ie->value.choice.SecurityInformation)); break; case E1AP_ProtocolIE_ID_id_UEDLAggregateMaximumBitRate: @@ -730,12 +786,7 @@ static bool e1_decode_pdu_session_setup_item(pdu_session_setup_t *pduSetup, E1AP pduSetup->id = in->pDU_Session_ID; pduSetup->numDRBSetup = in->dRB_Setup_List_NG_RAN.list.count; // NG DL UP Transport Layer Information (M) - E1AP_UP_TNL_Information_t *DL_UP_TNL_Info = &in->nG_DL_UP_TNL_Information; - if (DL_UP_TNL_Info->choice.gTPTunnel) { - _E1_EQ_CHECK_INT(DL_UP_TNL_Info->present, E1AP_UP_TNL_Information_PR_gTPTunnel); - BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&DL_UP_TNL_Info->choice.gTPTunnel->transportLayerAddress, pduSetup->tl_info.tlAddress); - OCTET_STRING_TO_INT32(&DL_UP_TNL_Info->choice.gTPTunnel->gTP_TEID, pduSetup->tl_info.teId); - } + CHECK_E1AP_DEC(e1_decode_up_tnl_info(&pduSetup->tl_info, &in->nG_DL_UP_TNL_Information)); // DRB Setup List (1..) for (int j = 0; j < in->dRB_Setup_List_NG_RAN.list.count; j++) { DRB_nGRAN_setup_t *drbSetup = pduSetup->DRBnGRanList + j; @@ -743,23 +794,9 @@ static bool e1_decode_pdu_session_setup_item(pdu_session_setup_t *pduSetup, E1AP // DRB ID (M) drbSetup->id = drb->dRB_ID; // UL UP Parameters (M) - drbSetup->numUpParam = drb->uL_UP_Transport_Parameters.list.count; - for (int k = 0; k < drb->uL_UP_Transport_Parameters.list.count; k++) { - up_params_t *UL_UP_param = drbSetup->UpParamList + k; - // UP Parameters List (1..) - E1AP_UP_Parameters_Item_t *UL_UP_item = drb->uL_UP_Transport_Parameters.list.array[k]; - // UP Transport Layer Information (M) - DevAssert(UL_UP_item->uP_TNL_Information.present == E1AP_UP_TNL_Information_PR_gTPTunnel); - // GTP Tunnel (M) - E1AP_GTPTunnel_t *gTPTunnel = UL_UP_item->uP_TNL_Information.choice.gTPTunnel; - AssertError(gTPTunnel != NULL, return false, "gTPTunnel information in required in UP Transport Layer Information\n"); - if (gTPTunnel) { - BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&gTPTunnel->transportLayerAddress, UL_UP_param->tl_info.tlAddress); - OCTET_STRING_TO_INT32(&gTPTunnel->gTP_TEID, UL_UP_param->tl_info.teId); - } else { - } - // Cell Group ID (M) - UL_UP_param->cell_group_id = UL_UP_item->cell_Group_ID; + if ((drbSetup->numUpParam = decode_dl_up_parameters(drbSetup->UpParamList, &drb->uL_UP_Transport_Parameters)) < 0) { + PRINT_ERROR("UL UP Parameters decoding failed\n"); + return false; } // Flow Setup List (M) drbSetup->numQosFlowSetup = drb->flow_Setup_List.list.count; @@ -769,6 +806,17 @@ static bool e1_decode_pdu_session_setup_item(pdu_session_setup_t *pduSetup, E1AP qosflowSetup->qfi = in_qosflowSetup->qoS_Flow_Identifier; } } + // DRB Failed to Setup List (O) + if (in->dRB_Failed_List_NG_RAN) { + for (int j = 0; j < in->dRB_Failed_List_NG_RAN->list.count; j++) { + DRB_nGRAN_failed_t *drbFailed = pduSetup->DRBnGRanFailedList + j; + E1AP_DRB_Failed_Item_NG_RAN_t *item = in->dRB_Failed_List_NG_RAN->list.array[j]; + // DRB ID (M) + drbFailed->id = item->dRB_ID; + // Cause (M) + drbFailed->cause = e1_decode_cause_ie(&item->cause); + } + } return true; } @@ -780,23 +828,14 @@ static bool e1_encode_pdu_session_setup_item(E1AP_PDU_Session_Resource_Setup_Ite // PDU Session ID (M) item->pDU_Session_ID = in->id; // NG DL UP Transport Layer Information (M) - item->nG_DL_UP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; - asn1cCalloc(item->nG_DL_UP_TNL_Information.choice.gTPTunnel, gTPTunnel); - TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(in->tl_info.tlAddress, &gTPTunnel->transportLayerAddress); - INT32_TO_OCTET_STRING(in->tl_info.teId, &gTPTunnel->gTP_TEID); + item->nG_DL_UP_TNL_Information = e1_encode_up_tnl_info(&in->tl_info); // DRB Setup List (1..) for (const DRB_nGRAN_setup_t *j = in->DRBnGRanList; j < in->DRBnGRanList + in->numDRBSetup; j++) { asn1cSequenceAdd(item->dRB_Setup_List_NG_RAN.list, E1AP_DRB_Setup_Item_NG_RAN_t, ieC3_1_1); // DRB ID (M) ieC3_1_1->dRB_ID = j->id; // UL UP Parameters (M) - for (const up_params_t *k = j->UpParamList; k < j->UpParamList + j->numUpParam; k++) { - asn1cSequenceAdd(ieC3_1_1->uL_UP_Transport_Parameters.list, E1AP_UP_Parameters_Item_t, ieC3_1_1_1); - ieC3_1_1_1->uP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; - asn1cCalloc(ieC3_1_1_1->uP_TNL_Information.choice.gTPTunnel, gTPTunnel); - TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(k->tl_info.tlAddress, &gTPTunnel->transportLayerAddress); - INT32_TO_OCTET_STRING(k->tl_info.teId, &gTPTunnel->gTP_TEID); - } + ieC3_1_1->uL_UP_Transport_Parameters = encode_dl_up_parameters(j->numUpParam, j->UpParamList); // Flow Setup List(M) for (const qos_flow_list_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlowSetup; k++) { asn1cSequenceAdd(ieC3_1_1->flow_Setup_List.list, E1AP_QoS_Flow_Item_t, ieC3_1_1_1); @@ -860,7 +899,7 @@ E1AP_E1AP_PDU_t *encode_E1_bearer_context_setup_response(const e1ap_bearer_setup E1AP_PDU_Session_Resource_Setup_List_t *pduSetup = &msgNGRAN->value.choice.PDU_Session_Resource_Setup_List; for (const pdu_session_setup_t *i = in->pduSession; i < in->pduSession + in->numPDUSessions; i++) { asn1cSequenceAdd(pduSetup->list, E1AP_PDU_Session_Resource_Setup_Item_t, ieC3_1); - e1_encode_pdu_session_setup_item(ieC3_1, i); + CHECK_E1AP_DEC(e1_encode_pdu_session_setup_item(ieC3_1, i)); } return pdu; } @@ -913,7 +952,7 @@ bool decode_E1_bearer_context_setup_response(const E1AP_E1AP_PDU_t *pdu, e1ap_be for (int i = 0; i < pduSetupList->list.count; i++) { pdu_session_setup_t *pduSetup = out->pduSession + i; E1AP_PDU_Session_Resource_Setup_Item_t *pdu_session = pduSetupList->list.array[i]; - e1_decode_pdu_session_setup_item(pduSetup, pdu_session); + CHECK_E1AP_DEC(e1_decode_pdu_session_setup_item(pduSetup, pdu_session)); } } break; @@ -981,8 +1020,7 @@ bool eq_bearer_context_setup_response(const e1ap_bearer_setup_resp_t *a, const e const pdu_session_setup_t *ps_a = &a->pduSession[i]; const pdu_session_setup_t *ps_b = &b->pduSession[i]; _E1_EQ_CHECK_LONG(ps_a->id, ps_b->id); - _E1_EQ_CHECK_INT(ps_a->tl_info.tlAddress, ps_b->tl_info.tlAddress); - _E1_EQ_CHECK_LONG(ps_a->tl_info.teId, ps_b->tl_info.teId); + eq_up_tl_info(&ps_a->tl_info, &ps_b->tl_info); _E1_EQ_CHECK_INT(ps_a->numDRBSetup, ps_b->numDRBSetup); _E1_EQ_CHECK_INT(ps_a->numDRBFailed, ps_b->numDRBFailed); // Check DRB Setup From f2b80ae31cfdcc34c2e18256f213fd3f20733327 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Mon, 20 Jan 2025 17:36:16 +0100 Subject: [PATCH 34/48] Use cp_bearer_context_setup_request in cucp_cuup_e1ap.c --- openair2/RRC/NR/cucp_cuup_e1ap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR/cucp_cuup_e1ap.c b/openair2/RRC/NR/cucp_cuup_e1ap.c index 69cb92f50a..538cd3f524 100644 --- a/openair2/RRC/NR/cucp_cuup_e1ap.c +++ b/openair2/RRC/NR/cucp_cuup_e1ap.c @@ -26,6 +26,7 @@ #include "e1ap_messages_types.h" #include "intertask_interface.h" #include "nr_rrc_defs.h" +#include "E1AP/lib/e1ap_bearer_context_management.h" static void cucp_cuup_bearer_context_setup_e1ap(sctp_assoc_t assoc_id, const e1ap_bearer_setup_req_t *req) { @@ -33,8 +34,7 @@ static void cucp_cuup_bearer_context_setup_e1ap(sctp_assoc_t assoc_id, const e1a MessageDef *msg_p = itti_alloc_new_message(TASK_CUCP_E1, 0, E1AP_BEARER_CONTEXT_SETUP_REQ); msg_p->ittiMsgHeader.originInstance = assoc_id; e1ap_bearer_setup_req_t *bearer_req = &E1AP_BEARER_CONTEXT_SETUP_REQ(msg_p); - memcpy(bearer_req, req, sizeof(e1ap_bearer_setup_req_t)); - + *bearer_req = cp_bearer_context_setup_request(req); itti_send_msg_to_task (TASK_CUCP_E1, 0, msg_p); } From b061b5769855bf1566b2bf0a0d3c7bee7500e7c3 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 4 Feb 2025 11:14:23 +0100 Subject: [PATCH 35/48] Add Bearer Context Modification Request enc/dec lib --- .../E1AP/lib/e1ap_bearer_context_management.c | 632 +++++++++++++++++- .../E1AP/lib/e1ap_bearer_context_management.h | 6 + openair2/E1AP/lib/e1ap_lib_includes.h | 6 + 3 files changed, 643 insertions(+), 1 deletion(-) diff --git a/openair2/E1AP/lib/e1ap_bearer_context_management.c b/openair2/E1AP/lib/e1ap_bearer_context_management.c index e58fcbf145..a8f68e70d6 100644 --- a/openair2/E1AP/lib/e1ap_bearer_context_management.c +++ b/openair2/E1AP/lib/e1ap_bearer_context_management.c @@ -179,7 +179,7 @@ static bool eq_pdcp_config(const bearer_context_pdcp_config_t *a, const bearer_c /** @brief Encode SDAP Configuration IE */ static E1AP_SDAP_Configuration_t e1_encode_sdap_config(const bearer_context_sdap_config_t *in) { - E1AP_SDAP_Configuration_t out; + E1AP_SDAP_Configuration_t out = {0}; out.defaultDRB = in->defaultDRB ? E1AP_DefaultDRB_true : E1AP_DefaultDRB_false; out.sDAP_Header_UL = in->sDAP_Header_UL; out.sDAP_Header_DL = in->sDAP_Header_DL; @@ -1050,3 +1050,633 @@ void free_e1ap_context_setup_response(const e1ap_bearer_setup_resp_t *msg) { // Do nothing (no dynamic allocation) } + +/* ============================================ + * E1AP Bearer Context Modification Request + * ============================================ */ + +/** @brief Encode PDU Session Item to modify */ +static E1AP_PDU_Session_Resource_To_Modify_Item_t e1_encode_pdu_session_to_mod_item(const pdu_session_to_mod_t *in) +{ + E1AP_PDU_Session_Resource_To_Modify_Item_t out = {0}; + out.pDU_Session_ID = in->sessionId; + for (const DRB_nGRAN_to_mod_t *j = in->DRBnGRanModList; j < in->DRBnGRanModList + in->numDRB2Modify; j++) { + asn1cCalloc(out.dRB_To_Modify_List_NG_RAN, drb2Mod_List); + asn1cSequenceAdd(drb2Mod_List->list, E1AP_DRB_To_Modify_Item_NG_RAN_t, drb2Mod); + drb2Mod->dRB_ID = j->id; + // PDCP Config (O) + if (j->pdcp_config) { + asn1cCalloc(drb2Mod->pDCP_Configuration, pDCP_Configuration); + *pDCP_Configuration = e1_encode_pdcp_config(j->pdcp_config); + } + // SDAP Config (O) + if (j->sdap_config) { + asn1cCalloc(drb2Mod->sDAP_Configuration, sDAP_Configuration); + *sDAP_Configuration = e1_encode_sdap_config(j->sdap_config); + } + // PDCP SN Status Request (O) + if (j->pdcp_sn_status_requested) { + asn1cCalloc(drb2Mod->pDCP_SN_Status_Request, pDCP_SN_Status_Request); + *pDCP_SN_Status_Request = E1AP_PDCP_SN_Status_Request_requested; + } + // DL UP TNL parameters (O) + if (j->numDlUpParam > 0) { + asn1cCalloc(drb2Mod->dL_UP_Parameters, DL_UP_Param_List); + for (const up_params_t *k = j->DlUpParamList; k < j->DlUpParamList + j->numDlUpParam; k++) { + asn1cSequenceAdd(DL_UP_Param_List->list, E1AP_UP_Parameters_Item_t, DL_UP_Param); + DL_UP_Param->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info); + DL_UP_Param->cell_Group_ID = k->cell_group_id; + } + } + // QoS Flows to setup (O) + for (const qos_flow_to_setup_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlow2Setup; k++) { + asn1cCalloc(drb2Mod->flow_Mapping_Information, flow_Mapping_Information); + asn1cSequenceAdd(flow_Mapping_Information->list, E1AP_QoS_Flow_QoS_Parameter_Item_t, ie1_2); + *ie1_2 = e1_encode_qos_flow_to_setup(k); + } + } + return out; +} + +/** @brief Common encoding for the NG-RAN System IE */ +static E1AP_NG_RAN_BearerContextModificationRequest_t *encode_ng_ran_to_mod(E1AP_BearerContextModificationRequestIEs_t *ie) +{ + ie->id = E1AP_ProtocolIE_ID_id_System_BearerContextModificationRequest; + ie->criticality = E1AP_Criticality_reject; + ie->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_System_BearerContextModificationRequest; + E1AP_System_BearerContextModificationRequest_t *reqIE = &ie->value.choice.System_BearerContextModificationRequest; + reqIE->present = E1AP_System_BearerContextModificationRequest_PR_nG_RAN_BearerContextModificationRequest; + E1AP_ProtocolIE_Container_4932P26_t *msgNGRAN_list = calloc_or_fail(1, sizeof(*msgNGRAN_list)); + reqIE->choice.nG_RAN_BearerContextModificationRequest = (struct E1AP_ProtocolIE_Container *)msgNGRAN_list; + asn1cSequenceAdd(msgNGRAN_list->list, E1AP_NG_RAN_BearerContextModificationRequest_t, msgNGRAN); + return msgNGRAN; +} + +/** @brief Encode PDU Session Resource To Setup Modification List item */ +static E1AP_PDU_Session_Resource_To_Setup_Mod_Item_t e1_encode_pdu_session_to_setup_mod_item(const pdu_session_to_setup_t *in) +{ + E1AP_PDU_Session_Resource_To_Setup_Mod_Item_t item = {0}; + // PDU Session ID (M) + item.pDU_Session_ID = in->sessionId; + // PDU Session Type (M) + item.pDU_Session_Type = in->sessionType; + // SNSSAI (M) + item.sNSSAI = e1_encode_snssai(&in->nssai); + // Security indication (M) + item.securityIndication = e1_encode_security_indication(&in->securityIndication); + // NG UL UP Transport Layer Information (M) + item.nG_UL_UP_TNL_Information = e1_encode_up_tnl_info(&in->UP_TL_information); + // DRB to setup + for (const DRB_nGRAN_to_setup_t *j = in->DRBnGRanList; j < in->DRBnGRanList + in->numDRB2Setup; j++) { + asn1cSequenceAdd(item.dRB_To_Setup_Mod_List_NG_RAN.list, E1AP_DRB_To_Setup_Mod_Item_NG_RAN_t, ie1); + // DRB ID (M) + ie1->dRB_ID = j->id; + // SDAP Config (M) + ie1->sDAP_Configuration = e1_encode_sdap_config(&j->sdap_config); + // PDCP Config (M) + ie1->pDCP_Configuration = e1_encode_pdcp_config(&j->pdcp_config); + // Cell Group Information (M) + for (const cell_group_id_t *k = j->cellGroupList; k < j->cellGroupList + j->numCellGroups; k++) { + asn1cSequenceAdd(ie1->cell_Group_Information.list, E1AP_Cell_Group_Information_Item_t, ie1_1); + ie1_1->cell_Group_ID = *k; + } + // QoS Flows Information To Be Setup (M) + for (const qos_flow_to_setup_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlow2Setup; k++) { + asn1cSequenceAdd(ie1->flow_Mapping_Information.list, E1AP_QoS_Flow_QoS_Parameter_Item_t, ie1_2); + *ie1_2 = e1_encode_qos_flow_to_setup(k); + } + } + return item; +} + +/** + * @brief Encodes the E1 Bearer Modification Request message + */ +E1AP_E1AP_PDU_t *encode_E1_bearer_context_mod_request(const e1ap_bearer_mod_req_t *in) +{ + E1AP_E1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + + // PDU Type + pdu->present = E1AP_E1AP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, msg); + msg->procedureCode = E1AP_ProcedureCode_id_bearerContextModification; + msg->criticality = E1AP_Criticality_reject; + msg->value.present = E1AP_InitiatingMessage__value_PR_BearerContextModificationRequest; + + E1AP_BearerContextModificationRequest_t *out = &msg->value.choice.BearerContextModificationRequest; + + // gNB-CU-CP UE E1AP ID (M) + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ie1); + ie1->id = E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID; + ie1->criticality = E1AP_Criticality_reject; + ie1->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_CP_UE_E1AP_ID; + ie1->value.choice.GNB_CU_CP_UE_E1AP_ID = in->gNB_cu_cp_ue_id; + + // gNB-CU-UP UE E1AP ID (M) + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ie2); + ie2->id = E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID; + ie2->criticality = E1AP_Criticality_reject; + ie2->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_UP_UE_E1AP_ID; + ie2->value.choice.GNB_CU_UP_UE_E1AP_ID = in->gNB_cu_up_ue_id; + + // Security Information (O) + if (in->secInfo) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ie3); + ie3->id = E1AP_ProtocolIE_ID_id_SecurityInformation; + ie3->criticality = E1AP_Criticality_reject; + ie3->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_SecurityInformation; + ie3->value.choice.SecurityInformation = e1_encode_security_info(in->secInfo); + } + + // UE DL Aggregate Maximum Bit Rate (O) + if (in->ueDlAggMaxBitRate) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ie4); + ie4->id = E1AP_ProtocolIE_ID_id_UEDLAggregateMaximumBitRate; + ie4->criticality = E1AP_Criticality_reject; + ie4->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_BitRate; + INT32_TO_OCTET_STRING(*in->ueDlAggMaxBitRate, &ie4->value.choice.BitRate); + } + + // UE DL Maximum Integrity Protected Data Rate (O) + if (in->ueDlMaxIPBitRate) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ie5); + ie5->id = E1AP_ProtocolIE_ID_id_UEDLMaximumIntegrityProtectedDataRate; + ie5->criticality = E1AP_Criticality_ignore; + ie5->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_BitRate; + INT32_TO_OCTET_STRING(*in->ueDlMaxIPBitRate, &ie5->value.choice.BitRate); + } + + // Bearer Context Status Change (O) + if (in->bearerContextStatus && *in->bearerContextStatus == BEARER_SUSPEND) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ie6); + ie6->id = E1AP_ProtocolIE_ID_id_BearerContextStatusChange; + ie6->criticality = E1AP_Criticality_reject; + ie6->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_BearerContextStatusChange; + ie6->value.choice.BearerContextStatusChange = E1AP_BearerContextStatusChange_suspend; + } + + // UE Inactivity Timer (O) + if (in->inactivityTimer) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ie7); + ie7->id = E1AP_ProtocolIE_ID_id_UE_Inactivity_Timer; + ie7->criticality = E1AP_Criticality_ignore; + ie7->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_Inactivity_Timer; + ie7->value.choice.Inactivity_Timer = *in->inactivityTimer; + } + + // Data Discard Required (O) + if (in->dataDiscardRequired) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ie9); + ie9->id = E1AP_ProtocolIE_ID_id_DataDiscardRequired; + ie9->criticality = E1AP_Criticality_ignore; + ie9->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_DataDiscardRequired; + ie9->value.choice.DataDiscardRequired = in->dataDiscardRequired; + } + + // NG-RAN PDU Session Resource To Setup List (O) + if (in->numPDUSessions) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, setupList); + E1AP_NG_RAN_BearerContextModificationRequest_t *msgNGRAN = encode_ng_ran_to_mod(setupList); + msgNGRAN->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Setup_Mod_List; + msgNGRAN->criticality = E1AP_Criticality_reject; + msgNGRAN->value.present = E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Setup_Mod_List; + E1AP_PDU_Session_Resource_To_Setup_Mod_List_t *pdu2Setup = &msgNGRAN->value.choice.PDU_Session_Resource_To_Setup_Mod_List; + for (int i = 0; i < in->numPDUSessions; i++) { + const pdu_session_to_setup_t *pdu_session = &in->pduSession[i]; + asn1cSequenceAdd(pdu2Setup->list, E1AP_PDU_Session_Resource_To_Setup_Mod_Item_t, item); + *item = e1_encode_pdu_session_to_setup_mod_item(pdu_session); + } + } + + // NG-RAN PDU Session Resource To Modify List (O) + if (in->numPDUSessionsMod) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, modList); + E1AP_NG_RAN_BearerContextModificationRequest_t *msgNGRAN = encode_ng_ran_to_mod(modList); + msgNGRAN->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Modify_List; + msgNGRAN->criticality = E1AP_Criticality_reject; + msgNGRAN->value.present = E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Modify_List; + E1AP_PDU_Session_Resource_To_Modify_List_t *pdu2Setup = &msgNGRAN->value.choice.PDU_Session_Resource_To_Modify_List; + for (const pdu_session_to_mod_t *i = in->pduSessionMod; i < in->pduSessionMod + in->numPDUSessionsMod; i++) { + asn1cSequenceAdd(pdu2Setup->list, E1AP_PDU_Session_Resource_To_Modify_Item_t, ieC3_1); + *ieC3_1 = e1_encode_pdu_session_to_mod_item(i); + } + } + return pdu; +} + +/** @brief Decode PDU Session Item to Setup (Bearer Context Modification Request) */ +static bool e1_decode_pdu_session_to_setup_mod_item(pdu_session_to_setup_t *out, + const E1AP_PDU_Session_Resource_To_Setup_Mod_Item_t *item) +{ + // PDU Session ID (M) + out->sessionId = item->pDU_Session_ID; + // PDU Session Type (M) + out->sessionType = item->pDU_Session_Type; + // S-NSSAI (M) + CHECK_E1AP_DEC(e1_decode_snssai(&out->nssai, &item->sNSSAI)); + // Security Indication (M) + CHECK_E1AP_DEC(e1_decode_security_indication(&out->securityIndication, &item->securityIndication)); + /* NG UL UP Transport Layer Information (M) */ + _E1_EQ_CHECK_INT(item->nG_UL_UP_TNL_Information.present, E1AP_UP_TNL_Information_PR_gTPTunnel); + CHECK_E1AP_DEC(e1_decode_up_tnl_info(&out->UP_TL_information, &item->nG_UL_UP_TNL_Information)); + /* DRB To Setup List ( > 1 item ) */ + const E1AP_DRB_To_Setup_Mod_List_NG_RAN_t *drb2SetupList = &item->dRB_To_Setup_Mod_List_NG_RAN; + _E1_EQ_CHECK_GENERIC(drb2SetupList->list.count > 0, "%d", drb2SetupList->list.count, 0); + out->numDRB2Setup = drb2SetupList->list.count; + _E1_EQ_CHECK_INT(out->numDRB2Setup, 1); // can only handle one DRB per PDU session + for (int j = 0; j < drb2SetupList->list.count; j++) { + DRB_nGRAN_to_setup_t *drb = out->DRBnGRanList + j; + const E1AP_DRB_To_Setup_Mod_Item_NG_RAN_t *drb2Setup = drb2SetupList->list.array[j]; + // DRB ID (M) + drb->id = drb2Setup->dRB_ID; + // SDAP Configuration (M) + CHECK_E1AP_DEC(e1_decode_sdap_config(&drb->sdap_config, &drb2Setup->sDAP_Configuration)); + // PDCP Configuration (M) + CHECK_E1AP_DEC(e1_decode_pdcp_config(&drb->pdcp_config, &drb2Setup->pDCP_Configuration)); + // Cell Group Information (M) + const E1AP_Cell_Group_Information_t *cellGroupList = &drb2Setup->cell_Group_Information; + _E1_EQ_CHECK_GENERIC(cellGroupList->list.count > 0, "%d", cellGroupList->list.count, 0); + drb->numCellGroups = cellGroupList->list.count; + for (int k = 0; k < cellGroupList->list.count; k++) { + const E1AP_Cell_Group_Information_Item_t *cg2Setup = cellGroupList->list.array[k]; + // Cell Group ID + drb->cellGroupList[k] = cg2Setup->cell_Group_ID; + } + // QoS Flows Information To Be Setup (M) + const E1AP_QoS_Flow_QoS_Parameter_List_t *qos2SetupList = &drb2Setup->flow_Mapping_Information; + drb->numQosFlow2Setup = qos2SetupList->list.count; + for (int k = 0; k < qos2SetupList->list.count; k++) { + CHECK_E1AP_DEC(e1_decode_qos_flow_to_setup(drb->qosFlows + k, qos2SetupList->list.array[k])); + } + } + return true; +} + + +/** @brief Decode PDU Session Item to modify (Bearer Context Modification Request) */ +static bool e1_decode_pdu_session_to_mod_item(pdu_session_to_mod_t *out, const E1AP_PDU_Session_Resource_To_Modify_Item_t *in) +{ + // PDU Session ID + out->sessionId = in->pDU_Session_ID; + // DRB to modify list + E1AP_DRB_To_Modify_List_NG_RAN_t *drb2ModList = in->dRB_To_Modify_List_NG_RAN; + out->numDRB2Modify = drb2ModList->list.count; + for (int j = 0; j < drb2ModList->list.count; j++) { + DRB_nGRAN_to_mod_t *drb = out->DRBnGRanModList + j; + // DRB to modify item + E1AP_DRB_To_Modify_Item_NG_RAN_t *drb2Mod = drb2ModList->list.array[j]; + drb->id = drb2Mod->dRB_ID; + // PDCP Config (O) + if (drb2Mod->pDCP_Configuration) { + drb->pdcp_config = malloc_or_fail(sizeof(*drb->pdcp_config)); + CHECK_E1AP_DEC(e1_decode_pdcp_config(drb->pdcp_config, drb2Mod->pDCP_Configuration)); + } + // SDAP Config (O) + if (drb2Mod->sDAP_Configuration) { + drb->sdap_config = malloc_or_fail(sizeof(*drb->sdap_config)); + CHECK_E1AP_DEC(e1_decode_sdap_config(drb->sdap_config, drb2Mod->sDAP_Configuration)); + } + // PDCP SN Status Request (O) + if (drb2Mod->pDCP_SN_Status_Request) { + drb->pdcp_sn_status_requested = true; + } + // DL UP TNL parameters (O) + if (drb2Mod->dL_UP_Parameters) { + E1AP_UP_Parameters_t *dl_up_paramList = drb2Mod->dL_UP_Parameters; + if((drb->numDlUpParam = decode_dl_up_parameters(drb->DlUpParamList, dl_up_paramList)) < 0) { + PRINT_ERROR("Failed to decode DL UP TNL parameters\n"); + return false; + } + } + // QoS Flows Information To Be Setup (O) + E1AP_QoS_Flow_QoS_Parameter_List_t *qos2SetupList = drb2Mod->flow_Mapping_Information; + drb->numQosFlow2Setup = qos2SetupList->list.count; + for (int k = 0; k < qos2SetupList->list.count; k++) { + CHECK_E1AP_DEC(e1_decode_qos_flow_to_setup(drb->qosFlows + k, qos2SetupList->list.array[k])); + } + } + return true; +} + + +/** + * @brief E1 Bearer Modification Request message decoding + */ +bool decode_E1_bearer_context_mod_request(const E1AP_E1AP_PDU_t *pdu, e1ap_bearer_mod_req_t *out) +{ + DevAssert(pdu != NULL); + // Validate message type + _E1_EQ_CHECK_INT(pdu->present, E1AP_E1AP_PDU_PR_initiatingMessage); + const E1AP_InitiatingMessage_t *im = pdu->choice.initiatingMessage; + _E1_EQ_CHECK_LONG(im->procedureCode, E1AP_ProcedureCode_id_bearerContextModification); + _E1_EQ_CHECK_INT(im->value.present, E1AP_InitiatingMessage__value_PR_BearerContextModificationRequest); + + const E1AP_BearerContextModificationRequest_t *in = &im->value.choice.BearerContextModificationRequest; + E1AP_BearerContextModificationRequestIEs_t *ie = NULL; + + // Mandatory IEs + // gNB-CU-CP UE E1AP ID (M) + E1AP_LIB_FIND_IE(E1AP_BearerContextModificationRequestIEs_t, ie, in, E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID, true); + // gNB-CU-UP UE E1AP ID (M) + E1AP_LIB_FIND_IE(E1AP_BearerContextModificationRequestIEs_t, ie, in, E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID, true); + + // Loop through all IEs and extract + for (int i = 0; i < in->protocolIEs.list.count; i++) { + ie = in->protocolIEs.list.array[i]; + + switch (ie->id) { + case E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID: + _E1_EQ_CHECK_INT(ie->value.present, E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_CP_UE_E1AP_ID); + out->gNB_cu_cp_ue_id = ie->value.choice.GNB_CU_CP_UE_E1AP_ID; + break; + + case E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID: + _E1_EQ_CHECK_INT(ie->value.present, E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_UP_UE_E1AP_ID); + out->gNB_cu_up_ue_id = ie->value.choice.GNB_CU_UP_UE_E1AP_ID; + break; + + case E1AP_ProtocolIE_ID_id_SecurityInformation: + out->secInfo = malloc_or_fail(sizeof(*out->secInfo)); + CHECK_E1AP_DEC(e1_decode_security_info(out->secInfo, &ie->value.choice.SecurityInformation)); + break; + + case E1AP_ProtocolIE_ID_id_UEDLAggregateMaximumBitRate: + out->ueDlAggMaxBitRate = malloc_or_fail(sizeof(*out->ueDlAggMaxBitRate)); + OCTET_STRING_TO_UINT32(&ie->value.choice.BitRate, *out->ueDlAggMaxBitRate); + break; + + case E1AP_ProtocolIE_ID_id_UEDLMaximumIntegrityProtectedDataRate: + out->ueDlMaxIPBitRate = malloc_or_fail(sizeof(*out->ueDlMaxIPBitRate)); + OCTET_STRING_TO_UINT32(&ie->value.choice.BitRate, *out->ueDlMaxIPBitRate); + break; + + case E1AP_ProtocolIE_ID_id_BearerContextStatusChange: + _E1_EQ_CHECK_INT(ie->value.present, E1AP_BearerContextModificationRequestIEs__value_PR_BearerContextStatusChange); + if (ie->value.choice.BearerContextStatusChange == E1AP_BearerContextStatusChange_suspend) { + out->bearerContextStatus = malloc_or_fail(sizeof(*out->bearerContextStatus)); + *out->bearerContextStatus = BEARER_SUSPEND; + } + break; + + case E1AP_ProtocolIE_ID_id_UE_Inactivity_Timer: + out->inactivityTimer = malloc_or_fail(sizeof(*out->inactivityTimer)); + *out->inactivityTimer = ie->value.choice.Inactivity_Timer; + break; + + case E1AP_ProtocolIE_ID_id_DataDiscardRequired: + out->dataDiscardRequired = ie->value.choice.DataDiscardRequired; + break; + + case E1AP_ProtocolIE_ID_id_System_BearerContextModificationRequest: { + _E1_EQ_CHECK_INT(ie->value.present, + E1AP_BearerContextModificationRequestIEs__value_PR_System_BearerContextModificationRequest); + E1AP_System_BearerContextModificationRequest_t *modReq = &ie->value.choice.System_BearerContextModificationRequest; + DevAssert(ie->value.choice.System_BearerContextModificationRequest.choice.nG_RAN_BearerContextModificationRequest != NULL); + // System IE can contain either UTRAN or NG-RAN + switch (modReq->present) { + case E1AP_System_BearerContextModificationRequest_PR_NOTHING: + PRINT_ERROR("System Bearer Context Setup Request: no choice present\n"); + break; + case E1AP_System_BearerContextModificationRequest_PR_e_UTRAN_BearerContextModificationRequest: + PRINT_ERROR("UTRAN in E1 Setup Modification Request not supported\n"); + return false; + break; + case E1AP_System_BearerContextModificationRequest_PR_nG_RAN_BearerContextModificationRequest: + // NG-RAN + AssertFatal(modReq->choice.nG_RAN_BearerContextModificationRequest != NULL, + "System_BearerContextSetupRequest->choice.nG_RAN_BearerContextSetupRequest shall not be null"); + E1AP_ProtocolIE_Container_4932P26_t *msgNGRAN_list = + (E1AP_ProtocolIE_Container_4932P26_t *)modReq->choice.nG_RAN_BearerContextModificationRequest; + // loop through all items in the NG-RAN list (to setup, to modify, etc) + for (int i = 0; i < msgNGRAN_list->list.count; i++) { + E1AP_NG_RAN_BearerContextModificationRequest_t *msgNGRAN = msgNGRAN_list->list.array[i]; + switch (msgNGRAN->id) { + case E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Setup_Mod_List: + _E1_EQ_CHECK_LONG(msgNGRAN->id, E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Setup_Mod_List); + _E1_EQ_CHECK_INT(msgNGRAN->value.present, + E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Setup_Mod_List); + E1AP_PDU_Session_Resource_To_Setup_Mod_List_t *setupList = + &msgNGRAN->value.choice.PDU_Session_Resource_To_Setup_Mod_List; + // PDU Session Resource To Modify List + out->numPDUSessions = setupList->list.count; + // Loop through all PDU sessions + for (int i = 0; i < setupList->list.count; i++) { + pdu_session_to_setup_t *pdu_session = out->pduSession + i; + E1AP_PDU_Session_Resource_To_Setup_Mod_Item_t *pdu2Setup = setupList->list.array[i]; + CHECK_E1AP_DEC(e1_decode_pdu_session_to_setup_mod_item(pdu_session, pdu2Setup)); + } + break; + case E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Modify_List: + /* PDU Session Resource To Modify List (see 9.3.3.11 of TS 38.463) */ + _E1_EQ_CHECK_INT(msgNGRAN->value.present, + E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Modify_List); + E1AP_PDU_Session_Resource_To_Modify_List_t *modList = &msgNGRAN->value.choice.PDU_Session_Resource_To_Modify_List; + out->numPDUSessionsMod = modList->list.count; + // Loop through all PDU sessions + for (int i = 0; i < modList->list.count; i++) { + CHECK_E1AP_DEC(e1_decode_pdu_session_to_mod_item(out->pduSessionMod + i, modList->list.array[i])); + } + break; + default: + PRINT_ERROR("Unknown msgNGRAN->id in E1 Setup Modification Request\n"); + return false; + break; + } + } + + break; + default: + PRINT_ERROR("System Bearer Context Modification Request: No valid choice present.\n"); + break; + } + + break; + } + + default: + PRINT_ERROR("System Bearer Context Modification Request: this IE is not implemented (or) invalid IE detected\n"); + return false; + break; + } + } + + return true; +} + +/** @brief Deep copy for DRB Item to modify */ +static DRB_nGRAN_to_mod_t cp_drb_to_mod_item(const DRB_nGRAN_to_mod_t *msg) +{ + DRB_nGRAN_to_mod_t cp = {0}; + // shallow copy + cp = *msg; + _E1_CP_OPTIONAL_IE(&cp, msg, sdap_config); + _E1_CP_OPTIONAL_IE(&cp, msg, pdcp_config); + return cp; +} + +/** @brief Deep copy for PDU Session Item to modify */ +static pdu_session_to_mod_t cp_pdu_session_to_mod_item(const pdu_session_to_mod_t *msg) +{ + pdu_session_to_mod_t cp = {0}; + // shallow copy + cp = *msg; + // DRB to setup list + for (int j = 0; j < msg->numDRB2Setup; j++) + cp.DRBnGRanList[j] = cp_drb_to_setup_item(&msg->DRBnGRanList[j]); + // DRB to modify list + for (int j = 0; j < msg->numDRB2Modify; j++) + cp.DRBnGRanModList[j] = cp_drb_to_mod_item(&msg->DRBnGRanModList[j]); + _E1_CP_OPTIONAL_IE(&cp, msg, securityIndication); + _E1_CP_OPTIONAL_IE(&cp, msg, UP_TL_information); + return cp; +} + +/** @brief Deep copy for Bearer Context Modification Request */ +e1ap_bearer_mod_req_t cp_bearer_context_mod_request(const e1ap_bearer_mod_req_t *msg) +{ + e1ap_bearer_mod_req_t cp = {0}; + // Shallow copy + cp = *msg; + // Deep copy for optional fields + _E1_CP_OPTIONAL_IE(&cp, msg, secInfo); + _E1_CP_OPTIONAL_IE(&cp, msg, bearerContextStatus); + _E1_CP_OPTIONAL_IE(&cp, msg, inactivityTimer); + // Deep copy PDU sessions + for (int i = 0; i < msg->numPDUSessions; i++) + cp.pduSession[i] = cp_pdu_session_item(&msg->pduSession[i]); + for (int i = 0; i < msg->numPDUSessionsMod; i++) + cp.pduSessionMod[i] = cp_pdu_session_to_mod_item(&msg->pduSessionMod[i]); + return cp; +} + +/** @brief Equality check for Security Indication IE (3GPP TS 38.463 9.3.1.23) */ +static bool eq_security_ind(security_indication_t *a, security_indication_t *b) +{ + _E1_EQ_CHECK_INT(a->confidentialityProtectionIndication, b->confidentialityProtectionIndication); + _E1_EQ_CHECK_INT(a->integrityProtectionIndication, b->integrityProtectionIndication); + _E1_EQ_CHECK_LONG(a->maxIPrate, b->maxIPrate); + return true; +} + +/** + * @brief Equality check for DRB_nGRAN_to_mod_t + */ +static bool eq_drb_to_mod(const DRB_nGRAN_to_mod_t *a, const DRB_nGRAN_to_mod_t *b) +{ + bool result = true; + _E1_EQ_CHECK_LONG(a->id, b->id); + _E1_EQ_CHECK_INT(a->numQosFlow2Setup, b->numQosFlow2Setup); + for (int i = 0; i < a->numQosFlow2Setup; i++) { + result &= eq_qos_flow(&a->qosFlows[i], &b->qosFlows[i]); + } + _E1_EQ_CHECK_INT(a->numDlUpParam, b->numDlUpParam); + for (int i = 0; i < a->numDlUpParam; i++) { + _E1_EQ_CHECK_INT(a->DlUpParamList[i].cell_group_id, b->DlUpParamList[i].cell_group_id); + eq_up_tl_info(&a->DlUpParamList[i].tl_info, &b->DlUpParamList[i].tl_info); + } + if (a->pdcp_config && b->pdcp_config) + result &= eq_pdcp_config(a->pdcp_config, b->pdcp_config); + if (a->sdap_config && b->sdap_config) + result &= eq_sdap_config(a->sdap_config, b->sdap_config); + return result; +} + +/** @brief Equality check for PDU session item to modify */ +static bool eq_pdu_session_to_mod_item(const pdu_session_to_mod_t *a, const pdu_session_to_mod_t *b) +{ + _E1_EQ_CHECK_LONG(a->sessionId, b->sessionId); + if (a->UP_TL_information && a->UP_TL_information) { + if (!eq_up_tl_info(a->UP_TL_information, b->UP_TL_information)) + return false; + } + _E1_EQ_CHECK_INT(a->numDRB2Setup, b->numDRB2Setup); + for (int i = 0; i < a->numDRB2Setup; i++) { + if (!eq_drb_to_setup(&a->DRBnGRanList[i], &b->DRBnGRanList[i])) + return false; + } + _E1_EQ_CHECK_LONG(a->numDRB2Modify, b->numDRB2Modify); + for (int i = 0; i < a->numDRB2Modify; i++) { + if (!eq_drb_to_mod(&a->DRBnGRanModList[i], &b->DRBnGRanModList[i])) + return false; + } + if (a->securityIndication && b->securityIndication) { + eq_security_ind(a->securityIndication, b->securityIndication); + } + return true; +} + +/** + * @brief E1AP Bearer Modification Request equality check + */ +bool eq_bearer_context_mod_request(const e1ap_bearer_mod_req_t *a, const e1ap_bearer_mod_req_t *b) +{ + // basic members + _E1_EQ_CHECK_INT(a->gNB_cu_cp_ue_id, b->gNB_cu_cp_ue_id); + _E1_EQ_CHECK_INT(a->gNB_cu_up_ue_id, b->gNB_cu_up_ue_id); + _E1_EQ_CHECK_INT(a->numPDUSessions, b->numPDUSessions); + _E1_EQ_CHECK_INT(a->numPDUSessionsMod, b->numPDUSessionsMod); + // Security Info + _E1_EQ_CHECK_OPTIONAL_PTR(a, b, secInfo); + if (a->secInfo && b->secInfo) { + _E1_EQ_CHECK_LONG(a->secInfo->cipheringAlgorithm, b->secInfo->cipheringAlgorithm); + _E1_EQ_CHECK_LONG(a->secInfo->integrityProtectionAlgorithm, b->secInfo->integrityProtectionAlgorithm); + _E1_EQ_CHECK_STR(a->secInfo->encryptionKey, b->secInfo->encryptionKey); + _E1_EQ_CHECK_STR(a->secInfo->integrityProtectionKey, b->secInfo->integrityProtectionKey); + } + // Bearer Context Status (O) + _E1_EQ_CHECK_OPTIONAL_IE(a, b, bearerContextStatus, _E1_EQ_CHECK_INT); + // Inactivity Timer (O) + _E1_EQ_CHECK_OPTIONAL_PTR(a, b, inactivityTimer); + if (a->inactivityTimer && b->inactivityTimer) + _E1_EQ_CHECK_INT(*a->inactivityTimer, *b->inactivityTimer); + // Activity Notification Level (O) + // PDU Sessions + if (a->numPDUSessions != b->numPDUSessions) + return false; + for (int i = 0; i < a->numPDUSessions; i++) { + if (!eq_pdu_session_item(&a->pduSession[i], &b->pduSession[i])) + return false; + } + // Modified PDU Sessions + if (a->numPDUSessionsMod != b->numPDUSessionsMod) + return false; + for (int i = 0; i < a->numPDUSessionsMod; i++) { + if (!eq_pdu_session_to_mod_item(&a->pduSessionMod[i], &b->pduSessionMod[i])) + return false; + } + return true; +} + +/* Free DRB to modify item */ +static void free_drb_to_mod_item(const DRB_nGRAN_to_mod_t *msg) +{ + free(msg->pdcp_config); + free(msg->sdap_config); +} + +/* Free PDU Session to modify item */ +void free_pdu_session_to_mod_item(const pdu_session_to_mod_t *msg) +{ + free(msg->securityIndication); + free(msg->UP_TL_information); + for (int i = 0; i < msg->numDRB2Modify; i++) + free_drb_to_mod_item(&msg->DRBnGRanModList[i]); + for (int i = 0; i < msg->numDRB2Setup; i++) + free_drb_to_setup_item(&msg->DRBnGRanList[i]); +} + +/** + * @brief E1AP Bearer Context Modification Request memory management + */ +void free_e1ap_context_mod_request(const e1ap_bearer_mod_req_t *msg) +{ + free(msg->secInfo); + free(msg->ueDlAggMaxBitRate); + free(msg->ueDlMaxIPBitRate); + free(msg->bearerContextStatus); + free(msg->inactivityTimer); + for (int i = 0; i < msg->numPDUSessions; i++) + free_pdu_session_to_setup_item(&msg->pduSession[i]); + for (int i = 0; i < msg->numPDUSessionsMod; i++) + free_pdu_session_to_mod_item(&msg->pduSessionMod[i]); +} diff --git a/openair2/E1AP/lib/e1ap_bearer_context_management.h b/openair2/E1AP/lib/e1ap_bearer_context_management.h index 95e4cb0e15..132c1857e1 100644 --- a/openair2/E1AP/lib/e1ap_bearer_context_management.h +++ b/openair2/E1AP/lib/e1ap_bearer_context_management.h @@ -40,4 +40,10 @@ void free_e1ap_context_setup_response(const e1ap_bearer_setup_resp_t *msg); e1ap_bearer_setup_resp_t cp_bearer_context_setup_response(const e1ap_bearer_setup_resp_t *msg); bool eq_bearer_context_setup_response(const e1ap_bearer_setup_resp_t *a, const e1ap_bearer_setup_resp_t *b); +struct E1AP_E1AP_PDU *encode_E1_bearer_context_mod_request(const e1ap_bearer_mod_req_t *msg); +bool decode_E1_bearer_context_mod_request(const struct E1AP_E1AP_PDU *pdu, e1ap_bearer_mod_req_t *out); +void free_e1ap_context_mod_request(const e1ap_bearer_mod_req_t *msg); +e1ap_bearer_mod_req_t cp_bearer_context_mod_request(const e1ap_bearer_mod_req_t *msg); +bool eq_bearer_context_mod_request(const e1ap_bearer_mod_req_t *a, const e1ap_bearer_mod_req_t *b); + #endif /* E1AP_BEARER_CONTEXT_MANAGEMENT_H_ */ diff --git a/openair2/E1AP/lib/e1ap_lib_includes.h b/openair2/E1AP/lib/e1ap_lib_includes.h index 785fd9d09c..46d09f5aa9 100644 --- a/openair2/E1AP/lib/e1ap_lib_includes.h +++ b/openair2/E1AP/lib/e1ap_lib_includes.h @@ -67,5 +67,11 @@ #include "E1AP_Transport-UP-Layer-Addresses-Info-To-Remove-Item.h" #include "E1AP_GTPTLAs.h" #include "E1AP_GTPTLA-Item.h" +// E1 Bearer Context Modification Request +#include "E1AP_PDU-Session-Resource-To-Modify-Item.h" +#include "E1AP_DRB-To-Modify-List-NG-RAN.h" +#include "E1AP_DRB-To-Modify-Item-NG-RAN.h" +#include "E1AP_PDU-Session-Resource-To-Setup-Mod-Item.h" +#include "E1AP_DRB-To-Setup-Mod-Item-NG-RAN.h" #endif /* E1AP_LIB_INCLUDES_H_ */ From b854f971306f4c780b6d5aff228c4938ebcf8b89 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 11 Feb 2025 13:15:01 +0100 Subject: [PATCH 36/48] Enable pDCP_Reestablishment in test_bearer_context_setup_request --- openair2/E1AP/tests/e1ap_lib_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/E1AP/tests/e1ap_lib_test.c b/openair2/E1AP/tests/e1ap_lib_test.c index bc670486cd..368698a517 100644 --- a/openair2/E1AP/tests/e1ap_lib_test.c +++ b/openair2/E1AP/tests/e1ap_lib_test.c @@ -85,7 +85,7 @@ static void test_bearer_context_setup_request(void) { bearer_context_pdcp_config_t pdcp = { .discardTimer = E1AP_DiscardTimer_ms10, - .pDCP_Reestablishment = E1AP_PDCP_Reestablishment_true, + .pDCP_Reestablishment = true, .pDCP_SN_Size_DL = E1AP_PDCP_SN_Size_s_12, .pDCP_SN_Size_UL = E1AP_PDCP_SN_Size_s_12, .reorderingTimer = 10, From 9157e22c7c7072f45c5d9fa8cf5ba190c17cd9ad Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Feb 2025 11:37:06 +0100 Subject: [PATCH 37/48] Add F1 Reset enc/dec/cp/eq/free library and test --- openair2/COMMON/f1ap_messages_types.h | 11 +- openair2/F1AP/lib/f1ap_interface_management.c | 220 ++++++++++++++++++ openair2/F1AP/lib/f1ap_interface_management.h | 7 + openair2/F1AP/lib/f1ap_lib_common.c | 55 +++++ openair2/F1AP/lib/f1ap_lib_common.h | 6 + openair2/F1AP/lib/f1ap_lib_includes.h | 1 + openair2/F1AP/tests/f1ap_lib_test.c | 66 ++++++ 7 files changed, 362 insertions(+), 4 deletions(-) diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index 36c3ad4f76..e72741f6ed 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -563,15 +563,18 @@ typedef enum F1AP_ResetType_e { F1AP_RESET_PART_OF_F1_INTERFACE } f1ap_ResetType_t; +typedef struct f1ap_ue_to_reset_t { + uint32_t *gNB_CU_ue_id; + uint32_t *gNB_DU_ue_id; +} f1ap_ue_to_reset_t; + typedef struct f1ap_reset_t { uint64_t transaction_id; f1ap_Cause_t cause; long cause_value; f1ap_ResetType_t reset_type; - struct { - uint32_t gNB_CU_ue_id; - uint32_t gNB_DU_ue_id; - } ue_to_reset[F1AP_MAX_NO_OF_INDIVIDUAL_CONNECTIONS_TO_RESET]; + int num_ue_to_reset; + f1ap_ue_to_reset_t *ue_to_reset; // array of num_ue_to_reset elements } f1ap_reset_t; typedef struct f1ap_reset_ack_t { diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c index 182f40952d..ce7ece3c11 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.c +++ b/openair2/F1AP/lib/f1ap_interface_management.c @@ -32,6 +32,226 @@ #include "f1ap_messages_types.h" #include "f1ap_lib_extern.h" +F1AP_UE_associatedLogicalF1_ConnectionItem_t encode_f1ap_ue_to_reset(const f1ap_ue_to_reset_t *to_reset) +{ + F1AP_UE_associatedLogicalF1_ConnectionItem_t conn_it = {0}; + + if (to_reset->gNB_CU_ue_id) + asn1cCallocOne(conn_it.gNB_CU_UE_F1AP_ID, *to_reset->gNB_CU_ue_id); + if (to_reset->gNB_DU_ue_id) + asn1cCallocOne(conn_it.gNB_DU_UE_F1AP_ID, *to_reset->gNB_DU_ue_id); + + return conn_it; +} + +f1ap_ue_to_reset_t decode_f1ap_ue_to_reset(const F1AP_UE_associatedLogicalF1_ConnectionItem_t *conn_it) +{ + f1ap_ue_to_reset_t to_reset = {0}; + if (conn_it->gNB_CU_UE_F1AP_ID) { + to_reset.gNB_CU_ue_id = malloc_or_fail(sizeof(*to_reset.gNB_CU_ue_id)); + *to_reset.gNB_CU_ue_id = *conn_it->gNB_CU_UE_F1AP_ID; + } + if (conn_it->gNB_DU_UE_F1AP_ID) { + to_reset.gNB_DU_ue_id = malloc_or_fail(sizeof(*to_reset.gNB_DU_ue_id)); + *to_reset.gNB_DU_ue_id = *conn_it->gNB_DU_UE_F1AP_ID; + } + return to_reset; +} + +void free_f1ap_ue_to_reset(const f1ap_ue_to_reset_t *to_reset) +{ + free(to_reset->gNB_CU_ue_id); + free(to_reset->gNB_DU_ue_id); +} + +bool eq_f1ap_ue_to_reset(const f1ap_ue_to_reset_t *a, const f1ap_ue_to_reset_t *b) +{ + if ((!a->gNB_CU_ue_id) ^ (!b->gNB_CU_ue_id)) + return false; + if (a->gNB_CU_ue_id) + _F1_EQ_CHECK_INT(*a->gNB_CU_ue_id, *b->gNB_CU_ue_id); + if ((!a->gNB_DU_ue_id) ^ (!b->gNB_DU_ue_id)) + return false; + if (a->gNB_DU_ue_id) + _F1_EQ_CHECK_INT(*a->gNB_DU_ue_id, *b->gNB_DU_ue_id); + return true; +} + +f1ap_ue_to_reset_t cp_f1ap_ue_to_reset(const f1ap_ue_to_reset_t *orig) +{ + f1ap_ue_to_reset_t cp = {0}; + if (orig->gNB_CU_ue_id) { + cp.gNB_CU_ue_id = malloc_or_fail(sizeof(*cp.gNB_CU_ue_id)); + *cp.gNB_CU_ue_id = *orig->gNB_CU_ue_id; + } + if (orig->gNB_DU_ue_id) { + cp.gNB_DU_ue_id = malloc_or_fail(sizeof(*cp.gNB_DU_ue_id)); + *cp.gNB_DU_ue_id = *orig->gNB_DU_ue_id; + } + return cp; +} + +/* @brief encode F1 Reset (9.2.1.1 in TS 38.473) */ +F1AP_F1AP_PDU_t *encode_f1ap_reset(const f1ap_reset_t *msg) +{ + F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + /* Create Message Type */ + pdu->present = F1AP_F1AP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, initMsg); + initMsg->procedureCode = F1AP_ProcedureCode_id_Reset; + initMsg->criticality = F1AP_Criticality_reject; + initMsg->value.present = F1AP_InitiatingMessage__value_PR_Reset; + F1AP_Reset_t *reset = &initMsg->value.choice.Reset; + + /* (M) Transaction ID */ + asn1cSequenceAdd(reset->protocolIEs.list, F1AP_ResetIEs_t, ieC1); + ieC1->id = F1AP_ProtocolIE_ID_id_TransactionID; + ieC1->criticality = F1AP_Criticality_reject; + ieC1->value.present = F1AP_ResetIEs__value_PR_TransactionID; + ieC1->value.choice.TransactionID = msg->transaction_id; + + /* (M) Cause */ + asn1cSequenceAdd(reset->protocolIEs.list, F1AP_ResetIEs_t, ieC2); + ieC2->id = F1AP_ProtocolIE_ID_id_Cause; + ieC2->criticality = F1AP_Criticality_ignore; + ieC2->value.present = F1AP_ResetIEs__value_PR_Cause; + ieC2->value.choice.Cause = encode_f1ap_cause(msg->cause, msg->cause_value); + + /* (M) Reset type */ + asn1cSequenceAdd(reset->protocolIEs.list, F1AP_ResetIEs_t, ieC3); + ieC3->id = F1AP_ProtocolIE_ID_id_ResetType; + ieC3->criticality = F1AP_Criticality_reject; + ieC3->value.present = F1AP_ResetIEs__value_PR_ResetType; + if (msg->reset_type == F1AP_RESET_ALL) { + ieC3->value.choice.ResetType.present = F1AP_ResetType_PR_f1_Interface; + ieC3->value.choice.ResetType.choice.f1_Interface = F1AP_ResetAll_reset_all; + AssertFatal(msg->num_ue_to_reset == 0, "cannot have F1AP_RESET_ALL and %d UEs to reset\n", msg->num_ue_to_reset); + } else if (msg->reset_type == F1AP_RESET_PART_OF_F1_INTERFACE) { + F1AP_UE_associatedLogicalF1_ConnectionListRes_t *con_list = calloc_or_fail(1, sizeof(*con_list)); + ieC3->value.choice.ResetType.present = F1AP_ResetType_PR_partOfF1_Interface; + ieC3->value.choice.ResetType.choice.partOfF1_Interface = con_list; + AssertFatal(msg->num_ue_to_reset > 0, "at least one UE to reset required\n"); + for (int i = 0; i < msg->num_ue_to_reset; ++i) { + asn1cSequenceAdd(con_list->list, F1AP_UE_associatedLogicalF1_ConnectionItemRes_t, conn_it_res); + conn_it_res->id = F1AP_ProtocolIE_ID_id_UE_associatedLogicalF1_ConnectionItem; + conn_it_res->criticality = F1AP_Criticality_reject; + conn_it_res->value.present = F1AP_UE_associatedLogicalF1_ConnectionItemRes__value_PR_UE_associatedLogicalF1_ConnectionItem; + conn_it_res->value.choice.UE_associatedLogicalF1_ConnectionItem = encode_f1ap_ue_to_reset(&msg->ue_to_reset[i]); + } + } else { + AssertFatal(false, "illegal reset_type %d\n", msg->reset_type); + } + + return pdu; +} + +/* @brief decode F1 Reset (9.2.1.1 in TS 38.473) */ +bool decode_f1ap_reset(const F1AP_F1AP_PDU_t *pdu, f1ap_reset_t *out) +{ + _F1_EQ_CHECK_INT(pdu->present, F1AP_F1AP_PDU_PR_initiatingMessage); + AssertError(pdu->choice.initiatingMessage != NULL, return false, "pdu->choice.initiatingMessage is NULL"); + _F1_EQ_CHECK_LONG(pdu->choice.initiatingMessage->procedureCode, F1AP_ProcedureCode_id_Reset); + _F1_EQ_CHECK_INT(pdu->choice.initiatingMessage->value.present, F1AP_InitiatingMessage__value_PR_Reset); + + /* Check presence of mandatory IEs */ + F1AP_Reset_t *in = &pdu->choice.initiatingMessage->value.choice.Reset; + F1AP_ResetIEs_t *ie; + F1AP_LIB_FIND_IE(F1AP_ResetIEs_t, ie, in, F1AP_ProtocolIE_ID_id_TransactionID, true); + F1AP_LIB_FIND_IE(F1AP_ResetIEs_t, ie, in, F1AP_ProtocolIE_ID_id_Cause, true); + F1AP_LIB_FIND_IE(F1AP_ResetIEs_t, ie, in, F1AP_ProtocolIE_ID_id_ResetType, true); + + /* Loop over all IEs */ + for (int i = 0; i < in->protocolIEs.list.count; i++) { + AssertError(in->protocolIEs.list.array[i] != NULL, return false, "in->protocolIEs.list.array[i] is NULL"); + ie = in->protocolIEs.list.array[i]; + switch (ie->id) { + case F1AP_ProtocolIE_ID_id_TransactionID: + // (M) Transaction ID + _F1_EQ_CHECK_INT(ie->value.present, F1AP_ResetIEs__value_PR_TransactionID); + out->transaction_id = ie->value.choice.TransactionID; + break; + case F1AP_ProtocolIE_ID_id_Cause: + // (M) Cause + _F1_EQ_CHECK_INT(ie->value.present, F1AP_ResetIEs__value_PR_Cause); + if (!decode_f1ap_cause(ie->value.choice.Cause, &out->cause, &out->cause_value)) { + PRINT_ERROR("could not decode F1AP Cause\n"); + return false; + } + break; + case F1AP_ProtocolIE_ID_id_ResetType: + // (M) Reset type + _F1_EQ_CHECK_INT(ie->value.present, F1AP_ResetIEs__value_PR_ResetType); + if (ie->value.choice.ResetType.present == F1AP_ResetType_PR_f1_Interface) { + out->reset_type = F1AP_RESET_ALL; + } else if (ie->value.choice.ResetType.present == F1AP_ResetType_PR_partOfF1_Interface) { + out->reset_type = F1AP_RESET_PART_OF_F1_INTERFACE; + const F1AP_UE_associatedLogicalF1_ConnectionListRes_t *con_list = ie->value.choice.ResetType.choice.partOfF1_Interface; + AssertError(con_list->list.count > 0, return false, "no UEs for partially reset F1 interface\n"); + out->num_ue_to_reset = con_list->list.count; + out->ue_to_reset = calloc_or_fail(out->num_ue_to_reset, sizeof(*out->ue_to_reset)); + for (int i = 0; i < out->num_ue_to_reset; ++i) { + const F1AP_UE_associatedLogicalF1_ConnectionItemRes_t *it_res = + (const F1AP_UE_associatedLogicalF1_ConnectionItemRes_t *)con_list->list.array[i]; + _F1_EQ_CHECK_LONG(it_res->id, F1AP_ProtocolIE_ID_id_UE_associatedLogicalF1_ConnectionItem); + _F1_EQ_CHECK_INT(it_res->value.present, + F1AP_UE_associatedLogicalF1_ConnectionItemRes__value_PR_UE_associatedLogicalF1_ConnectionItem); + out->ue_to_reset[i] = decode_f1ap_ue_to_reset(&it_res->value.choice.UE_associatedLogicalF1_ConnectionItem); + } + } else { + PRINT_ERROR("unrecognized Reset type %d\n", ie->value.choice.ResetType.present); + return false; + } + } + } + return true; +} + +void free_f1ap_reset(f1ap_reset_t *msg) +{ + DevAssert(msg->reset_type == F1AP_RESET_ALL || msg->reset_type == F1AP_RESET_PART_OF_F1_INTERFACE); + if (msg->reset_type == F1AP_RESET_ALL) { + DevAssert(msg->ue_to_reset == NULL); + return; /* nothing to be freed in this case */ + } + for (int i = 0; i < msg->num_ue_to_reset; ++i) + free_f1ap_ue_to_reset(&msg->ue_to_reset[i]); + free(msg->ue_to_reset); +} + +bool eq_f1ap_reset(const f1ap_reset_t *a, const f1ap_reset_t *b) +{ + _F1_EQ_CHECK_LONG(a->transaction_id, b->transaction_id); + _F1_EQ_CHECK_INT(a->cause, b->cause); + _F1_EQ_CHECK_LONG(a->cause_value, b->cause_value); + _F1_EQ_CHECK_INT(a->reset_type, b->reset_type); + if (a->reset_type == F1AP_RESET_PART_OF_F1_INTERFACE) { + _F1_EQ_CHECK_INT(a->num_ue_to_reset, b->num_ue_to_reset); + for (int i = 0; i < a->num_ue_to_reset; ++i) + if (!eq_f1ap_ue_to_reset(&a->ue_to_reset[i], &b->ue_to_reset[i])) + return false; + } + return true; +} + +f1ap_reset_t cp_f1ap_reset(const f1ap_reset_t *orig) +{ + DevAssert(orig->reset_type == F1AP_RESET_ALL || orig->reset_type == F1AP_RESET_PART_OF_F1_INTERFACE); + f1ap_reset_t cp = { + .transaction_id = orig->transaction_id, + .cause = orig->cause, + .cause_value = orig->cause_value, + .reset_type = orig->reset_type, + }; + if (orig->reset_type == F1AP_RESET_PART_OF_F1_INTERFACE) { + DevAssert(orig->num_ue_to_reset > 0); + cp.num_ue_to_reset = orig->num_ue_to_reset; + cp.ue_to_reset = calloc_or_fail(cp.num_ue_to_reset, sizeof(*cp.ue_to_reset)); + for (int i = 0; i < cp.num_ue_to_reset; ++i) + cp.ue_to_reset[i] = cp_f1ap_ue_to_reset(&orig->ue_to_reset[i]); + } + return cp; +} + static const int nrb_lut[29] = {11, 18, 24, 25, 31, 32, 38, 51, 52, 65, 66, 78, 79, 93, 106, 107, 121, 132, 133, 135, 160, 162, 189, 216, 217, 245, 264, 270, 273}; diff --git a/openair2/F1AP/lib/f1ap_interface_management.h b/openair2/F1AP/lib/f1ap_interface_management.h index 6fd79b4007..b3769eabfe 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.h +++ b/openair2/F1AP/lib/f1ap_interface_management.h @@ -27,6 +27,13 @@ struct F1AP_F1AP_PDU; +/* F1 Reset */ +struct F1AP_F1AP_PDU *encode_f1ap_reset(const f1ap_reset_t *msg); +bool decode_f1ap_reset(const struct F1AP_F1AP_PDU *pdu, f1ap_reset_t *out); +void free_f1ap_reset(f1ap_reset_t *msg); +bool eq_f1ap_reset(const f1ap_reset_t *a, const f1ap_reset_t *b); +f1ap_reset_t cp_f1ap_reset(const f1ap_reset_t *orig); + struct F1AP_F1AP_PDU *encode_f1ap_setup_request(const f1ap_setup_req_t *msg); bool decode_f1ap_setup_request(const struct F1AP_F1AP_PDU *pdu, f1ap_setup_req_t *out); f1ap_setup_req_t cp_f1ap_setup_request(const f1ap_setup_req_t *msg); diff --git a/openair2/F1AP/lib/f1ap_lib_common.c b/openair2/F1AP/lib/f1ap_lib_common.c index a569146815..c49a87c57c 100644 --- a/openair2/F1AP/lib/f1ap_lib_common.c +++ b/openair2/F1AP/lib/f1ap_lib_common.c @@ -109,3 +109,58 @@ uint8_t *cp_octet_string(const OCTET_STRING_t *os, int *len) *len = os->size; return buf; } + +F1AP_Cause_t encode_f1ap_cause(f1ap_Cause_t cause, long cause_value) +{ + F1AP_Cause_t f1_cause = {0}; + switch (cause) { + case F1AP_CAUSE_RADIO_NETWORK: + f1_cause.present = F1AP_Cause_PR_radioNetwork; + f1_cause.choice.radioNetwork = cause_value; + break; + case F1AP_CAUSE_TRANSPORT: + f1_cause.present = F1AP_Cause_PR_transport; + f1_cause.choice.transport = cause_value; + break; + case F1AP_CAUSE_PROTOCOL: + f1_cause.present = F1AP_Cause_PR_protocol; + f1_cause.choice.protocol = cause_value; + break; + case F1AP_CAUSE_MISC: + f1_cause.present = F1AP_Cause_PR_misc; + f1_cause.choice.misc = cause_value; + break; + case F1AP_CAUSE_NOTHING: + default: + AssertFatal(false, "unknown cause value %d\n", cause); + break; + } + return f1_cause; +} + +bool decode_f1ap_cause(F1AP_Cause_t f1_cause, f1ap_Cause_t *cause, long *cause_value) +{ + switch (f1_cause.present) { + case F1AP_Cause_PR_radioNetwork: + *cause = F1AP_CAUSE_RADIO_NETWORK; + *cause_value = f1_cause.choice.radioNetwork; + break; + case F1AP_Cause_PR_transport: + *cause = F1AP_CAUSE_TRANSPORT; + *cause_value = f1_cause.choice.transport; + break; + case F1AP_Cause_PR_protocol: + *cause = F1AP_CAUSE_PROTOCOL; + *cause_value = f1_cause.choice.protocol; + break; + case F1AP_Cause_PR_misc: + *cause = F1AP_CAUSE_MISC; + *cause_value = f1_cause.choice.radioNetwork; + break; + case F1AP_Cause_PR_NOTHING: + default: + PRINT_ERROR("received illegal F1AP cause %d\n", f1_cause.present); + return false; + } + return true; +} diff --git a/openair2/F1AP/lib/f1ap_lib_common.h b/openair2/F1AP/lib/f1ap_lib_common.h index 2380a2da87..502200d9df 100644 --- a/openair2/F1AP/lib/f1ap_lib_common.h +++ b/openair2/F1AP/lib/f1ap_lib_common.h @@ -27,6 +27,9 @@ #include #include "openair3/UTILS/conversions.h" +#include "F1AP_Cause.h" +#include "f1ap_messages_types.h" + #ifdef ENABLE_TESTS #define PRINT_ERROR(...) fprintf(stderr, ##__VA_ARGS__) #else @@ -82,4 +85,7 @@ bool eq_f1ap_tx_bandwidth(const struct f1ap_transmission_bandwidth_t *a, const s struct OCTET_STRING; uint8_t *cp_octet_string(const struct OCTET_STRING *os, int *len); +F1AP_Cause_t encode_f1ap_cause(f1ap_Cause_t cause, long cause_value); +bool decode_f1ap_cause(F1AP_Cause_t f1_cause, f1ap_Cause_t *cause, long *cause_value); + #endif /* F1AP_LIB_COMMON_H_ */ diff --git a/openair2/F1AP/lib/f1ap_lib_includes.h b/openair2/F1AP/lib/f1ap_lib_includes.h index 6ff60a4897..267e7243e7 100644 --- a/openair2/F1AP/lib/f1ap_lib_includes.h +++ b/openair2/F1AP/lib/f1ap_lib_includes.h @@ -40,5 +40,6 @@ #include "F1AP_SuccessfulOutcome.h" #include "F1AP_SibtypetobeupdatedListItem.h" #include "F1AP_UnsuccessfulOutcome.h" +#include "F1AP_UE-associatedLogicalF1-ConnectionListRes.h" #endif /* F1AP_LIB_INCLUDES_H_ */ diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index 5d153f1aea..2e516a5607 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -383,6 +383,70 @@ static void test_f1ap_setup_failure(void) AssertFatal(ret, "eq_f1ap_setup_failure(): copied message doesn't match\n"); } +static void _test_f1ap_reset_msg(const f1ap_reset_t *orig) +{ + F1AP_F1AP_PDU_t *f1enc = encode_f1ap_reset(orig); + F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); + f1ap_msg_free(f1enc); + + f1ap_reset_t decoded = {0}; + bool ret = decode_f1ap_reset(f1dec, &decoded); + AssertFatal(ret, "decode_f1ap_reset(): could not decode message\n"); + f1ap_msg_free(f1dec); + + ret = eq_f1ap_reset(orig, &decoded); + AssertFatal(ret, "eq_f1ap_reset(): decoded message doesn't match original\n"); + free_f1ap_reset(&decoded); + + f1ap_reset_t cp = cp_f1ap_reset(orig); + ret = eq_f1ap_reset(orig, &cp); + AssertFatal(ret, "eq_f1ap_reset(): copied message doesn't match original\n"); + free_f1ap_reset(&cp); + + printf("f1ap_reset successful\n"); +} + +/** + * @brief Test F1AP Reset message where the entire F1 interface is reset + */ +static void test_f1ap_reset_all(void) +{ + f1ap_reset_t orig = { + .transaction_id = 2, + .cause = F1AP_CAUSE_TRANSPORT, + .cause_value = 3, /* no type -> whatever */ + .reset_type = F1AP_RESET_ALL, + }; + _test_f1ap_reset_msg(&orig); + free_f1ap_reset(&orig); +} + +/** + * @brief Test F1AP Reset message where only some UEs are marked to reset + */ +static void test_f1ap_reset_part(void) +{ + f1ap_reset_t orig = { + .transaction_id = 3, + .cause = F1AP_CAUSE_MISC, + .cause_value = 3, /* no type -> whatever */ + .reset_type = F1AP_RESET_PART_OF_F1_INTERFACE, + .num_ue_to_reset = 4, + }; + orig.ue_to_reset = calloc_or_fail(orig.num_ue_to_reset, sizeof(*orig.ue_to_reset)); + orig.ue_to_reset[0].gNB_CU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[0].gNB_CU_ue_id)); + *orig.ue_to_reset[0].gNB_CU_ue_id = 10; + orig.ue_to_reset[1].gNB_DU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[1].gNB_DU_ue_id)); + *orig.ue_to_reset[1].gNB_DU_ue_id = 11; + orig.ue_to_reset[2].gNB_CU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[2].gNB_CU_ue_id)); + *orig.ue_to_reset[2].gNB_CU_ue_id = 12; + orig.ue_to_reset[2].gNB_DU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[2].gNB_DU_ue_id)); + *orig.ue_to_reset[2].gNB_DU_ue_id = 13; + // orig.ue_to_reset[3] intentionally empty (because it is allowed, both IDs are optional) + _test_f1ap_reset_msg(&orig); + free_f1ap_reset(&orig); +} + /** * @brief Test F1 gNB-DU Configuration Update */ @@ -605,6 +669,8 @@ int main() test_f1ap_setup_request(); test_f1ap_setup_response(); test_f1ap_setup_failure(); + test_f1ap_reset_all(); + test_f1ap_reset_part(); test_f1ap_du_configuration_update(); test_f1ap_cu_configuration_update(); test_f1ap_cu_configuration_update_acknowledge(); From 4c9ed9d7ecfc5ed55f427df28b824b0c50e85af5 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Feb 2025 15:46:57 +0100 Subject: [PATCH 38/48] Use (CU-initiated) F1 Reset in stack --- openair2/F1AP/f1ap_cu_interface_management.c | 22 ++++++++++++++++++++ openair2/F1AP/f1ap_cu_interface_management.h | 1 + openair2/F1AP/f1ap_cu_task.c | 6 ++++++ openair2/RRC/NR/mac_rrc_dl_direct.c | 4 ++-- openair2/RRC/NR/mac_rrc_dl_f1ap.c | 7 +++++-- 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/openair2/F1AP/f1ap_cu_interface_management.c b/openair2/F1AP/f1ap_cu_interface_management.c index b81d81c6ba..8a6fd6311d 100644 --- a/openair2/F1AP/f1ap_cu_interface_management.c +++ b/openair2/F1AP/f1ap_cu_interface_management.c @@ -47,6 +47,28 @@ int CU_send_RESET_ACKNOWLEDGE(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack AssertFatal(1 == 0, "Not implemented yet\n"); } +void CU_send_RESET(sctp_assoc_t assoc_id, const f1ap_reset_t *reset) +{ + uint8_t *buffer = NULL; + uint32_t len = 0; + + /* Encode F1 Reset */ + F1AP_F1AP_PDU_t *pdu = encode_f1ap_reset(reset); + if (pdu == NULL) { + LOG_E(F1AP, "failed to create asn.1 message for f1ap reset\n"); + return; + } + + /* encode ASN.1 PER */ + int encoded = f1ap_encode_pdu(pdu, &buffer, &len); + ASN_STRUCT_FREE(asn_DEF_F1AP_F1AP_PDU, pdu); + if (encoded <= 0) { + LOG_E(F1AP, "Failed to encode F1 reset\n"); + return; + } + f1ap_itti_send_sctp_data_req(assoc_id, buffer, len); +} + /** * @brief F1AP Setup Request decoding (9.2.1.4 of 3GPP TS 38.473) and transfer to RRC */ diff --git a/openair2/F1AP/f1ap_cu_interface_management.h b/openair2/F1AP/f1ap_cu_interface_management.h index 9d0bf4a391..fb39f1ca53 100644 --- a/openair2/F1AP/f1ap_cu_interface_management.h +++ b/openair2/F1AP/f1ap_cu_interface_management.h @@ -36,6 +36,7 @@ /* * Reset */ +void CU_send_RESET(sctp_assoc_t assoc_id, const f1ap_reset_t *reset); int CU_handle_RESET_ACKNOWLEDGE(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu); int CU_send_RESET_ACKNOWLEDGE(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack); diff --git a/openair2/F1AP/f1ap_cu_task.c b/openair2/F1AP/f1ap_cu_task.c index fa77f818ea..deb272776f 100644 --- a/openair2/F1AP/f1ap_cu_task.c +++ b/openair2/F1AP/f1ap_cu_task.c @@ -35,6 +35,7 @@ #include "f1ap_cu_rrc_message_transfer.h" #include "f1ap_cu_ue_context_management.h" #include "lib/f1ap_rrc_message_transfer.h" +#include "lib/f1ap_interface_management.h" #include "f1ap_cu_paging.h" #include "f1ap_cu_task.h" #include @@ -151,6 +152,11 @@ void *F1AP_CU_task(void *arg) { &received_msg->ittiMsg.sctp_data_ind); break; + case F1AP_RESET: + CU_send_RESET(assoc_id, &F1AP_RESET(received_msg)); + free_f1ap_reset(&F1AP_RESET(received_msg)); + break; + case F1AP_RESET_ACK: CU_send_RESET_ACKNOWLEDGE(assoc_id, &F1AP_RESET_ACK(received_msg)); break; diff --git a/openair2/RRC/NR/mac_rrc_dl_direct.c b/openair2/RRC/NR/mac_rrc_dl_direct.c index 77113f6c8b..4f8d45ac8c 100644 --- a/openair2/RRC/NR/mac_rrc_dl_direct.c +++ b/openair2/RRC/NR/mac_rrc_dl_direct.c @@ -30,8 +30,8 @@ static void f1_reset_cu_initiated_direct(sctp_assoc_t assoc_id, const f1ap_reset_t *reset) { - (void)reset; - AssertFatal(false, "%s() not implemented yet\n", __func__); + AssertFatal(assoc_id == -1, "illegal assoc_id %d\n", assoc_id); + f1_reset_cu_initiated(reset); } static void f1_reset_acknowledge_du_initiated_direct(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack) diff --git a/openair2/RRC/NR/mac_rrc_dl_f1ap.c b/openair2/RRC/NR/mac_rrc_dl_f1ap.c index 8593ff816a..8a84a7f85d 100644 --- a/openair2/RRC/NR/mac_rrc_dl_f1ap.c +++ b/openair2/RRC/NR/mac_rrc_dl_f1ap.c @@ -35,8 +35,11 @@ static void f1_reset_cu_initiated_f1ap(sctp_assoc_t assoc_id, const f1ap_reset_t *reset) { - (void)reset; - AssertFatal(false, "%s() not implemented yet\n", __func__); + MessageDef *msg = itti_alloc_new_message(TASK_RRC_GNB, 0, F1AP_RESET); + msg->ittiMsgHeader.originInstance = assoc_id; + f1ap_reset_t *f1ap_msg = &F1AP_RESET(msg); + *f1ap_msg = cp_f1ap_reset(reset); + itti_send_msg_to_task(TASK_CU_F1, 0, msg); } static void f1_reset_acknowledge_du_initiated_f1ap(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack) From df365d9a2dbd0ac31f7c389cdef4e95008323356 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Feb 2025 15:41:44 +0100 Subject: [PATCH 39/48] Add F1 Reset Ack enc/dec/cp/eq/free library and test --- openair2/COMMON/f1ap_messages_types.h | 10 +- openair2/F1AP/lib/f1ap_interface_management.c | 115 ++++++++++++++++++ openair2/F1AP/lib/f1ap_interface_management.h | 7 ++ openair2/F1AP/tests/f1ap_lib_test.c | 45 +++++++ 4 files changed, 171 insertions(+), 6 deletions(-) diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index e72741f6ed..53740c1bde 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -578,12 +578,10 @@ typedef struct f1ap_reset_t { } f1ap_reset_t; typedef struct f1ap_reset_ack_t { - uint64_t transaction_id; - struct { - uint32_t gNB_CU_ue_id; - uint32_t gNB_DU_ue_id; - } ue_to_reset[F1AP_MAX_NO_OF_INDIVIDUAL_CONNECTIONS_TO_RESET]; - uint16_t criticality_diagnostics; + uint64_t transaction_id; + int num_ue_to_reset; + f1ap_ue_to_reset_t *ue_to_reset; // array of num_ue_to_reset elements + //uint16_t criticality_diagnostics; // not implemented as of now } f1ap_reset_ack_t; #endif /* F1AP_MESSAGES_TYPES_H_ */ diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c index ce7ece3c11..08f324a618 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.c +++ b/openair2/F1AP/lib/f1ap_interface_management.c @@ -252,6 +252,121 @@ f1ap_reset_t cp_f1ap_reset(const f1ap_reset_t *orig) return cp; } +/* @brief encode F1 Reset Ack (9.2.1.2 in TS 38.473) */ +struct F1AP_F1AP_PDU *encode_f1ap_reset_ack(const f1ap_reset_ack_t *msg) +{ + F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + /* Create Message Type */ + pdu->present = F1AP_F1AP_PDU_PR_successfulOutcome; + asn1cCalloc(pdu->choice.successfulOutcome, so); + so->procedureCode = F1AP_ProcedureCode_id_Reset; + so->criticality = F1AP_Criticality_reject; + so->value.present = F1AP_SuccessfulOutcome__value_PR_ResetAcknowledge; + F1AP_ResetAcknowledge_t *reset_ack = &so->value.choice.ResetAcknowledge; + + /* (M) Transaction ID */ + asn1cSequenceAdd(reset_ack->protocolIEs.list, F1AP_ResetAcknowledgeIEs_t, ieC1); + ieC1->id = F1AP_ProtocolIE_ID_id_TransactionID; + ieC1->criticality = F1AP_Criticality_reject; + ieC1->value.present = F1AP_ResetAcknowledgeIEs__value_PR_TransactionID; + ieC1->value.choice.TransactionID = msg->transaction_id; + + /* TODO criticality diagnostics */ + + /* 0:N UEs to reset */ + if (msg->num_ue_to_reset == 0) + return pdu; /* no UEs to encode */ + + asn1cSequenceAdd(reset_ack->protocolIEs.list, F1AP_ResetAcknowledgeIEs_t, ieC2); + ieC2->id = F1AP_ProtocolIE_ID_id_UE_associatedLogicalF1_ConnectionListResAck; + ieC2->criticality = F1AP_Criticality_ignore; + ieC2->value.present = F1AP_ResetAcknowledgeIEs__value_PR_UE_associatedLogicalF1_ConnectionListResAck; + F1AP_UE_associatedLogicalF1_ConnectionListResAck_t *ue_to_reset = &ieC2->value.choice.UE_associatedLogicalF1_ConnectionListResAck; + for (int i = 0; i < msg->num_ue_to_reset; ++i) { + asn1cSequenceAdd(ue_to_reset->list, F1AP_UE_associatedLogicalF1_ConnectionItemResAck_t, conn_it_res); + conn_it_res->id = F1AP_ProtocolIE_ID_id_UE_associatedLogicalF1_ConnectionItem; + conn_it_res->criticality = F1AP_Criticality_ignore; + conn_it_res->value.present = F1AP_UE_associatedLogicalF1_ConnectionItemResAck__value_PR_UE_associatedLogicalF1_ConnectionItem; + conn_it_res->value.choice.UE_associatedLogicalF1_ConnectionItem = encode_f1ap_ue_to_reset(&msg->ue_to_reset[i]); + } + return pdu; +} + +/* @brief decode F1 Reset Ack (9.2.1.2 in TS 38.473) */ +bool decode_f1ap_reset_ack(const struct F1AP_F1AP_PDU *pdu, f1ap_reset_ack_t *out) +{ + _F1_EQ_CHECK_INT(pdu->present, F1AP_F1AP_PDU_PR_successfulOutcome); + AssertError(pdu->choice.successfulOutcome != NULL, return false, "pdu->choice.initiatingMessage is NULL"); + _F1_EQ_CHECK_LONG(pdu->choice.successfulOutcome->procedureCode, F1AP_ProcedureCode_id_Reset); + _F1_EQ_CHECK_INT(pdu->choice.successfulOutcome->value.present, F1AP_SuccessfulOutcome__value_PR_ResetAcknowledge); + + /* Check presence of mandatory IEs */ + F1AP_ResetAcknowledge_t *in = &pdu->choice.successfulOutcome->value.choice.ResetAcknowledge; + F1AP_ResetAcknowledgeIEs_t *ie; + F1AP_LIB_FIND_IE(F1AP_ResetAcknowledgeIEs_t, ie, in, F1AP_ProtocolIE_ID_id_TransactionID, true); + + /* Loop over all IEs */ + for (int i = 0; i < in->protocolIEs.list.count; i++) { + AssertError(in->protocolIEs.list.array[i] != NULL, return false, "in->protocolIEs.list.array[i] is NULL"); + ie = in->protocolIEs.list.array[i]; + switch (ie->id) { + case F1AP_ProtocolIE_ID_id_TransactionID: + // (M) Transaction ID + _F1_EQ_CHECK_INT(ie->value.present, F1AP_ResetAcknowledgeIEs__value_PR_TransactionID); + out->transaction_id = ie->value.choice.TransactionID; + break; + case F1AP_ProtocolIE_ID_id_UE_associatedLogicalF1_ConnectionListResAck: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_ResetAcknowledgeIEs__value_PR_UE_associatedLogicalF1_ConnectionListResAck); + { + const F1AP_UE_associatedLogicalF1_ConnectionListResAck_t *conn_list = &ie->value.choice.UE_associatedLogicalF1_ConnectionListResAck; + AssertError(conn_list->list.count > 0, return false, "no UEs for partially reset F1 interface\n"); + out->num_ue_to_reset = conn_list->list.count; + out->ue_to_reset = calloc_or_fail(out->num_ue_to_reset, sizeof(*out->ue_to_reset)); + for (int i = 0; i < out->num_ue_to_reset; ++i) { + const F1AP_UE_associatedLogicalF1_ConnectionItemResAck_t *it_res = (const F1AP_UE_associatedLogicalF1_ConnectionItemResAck_t *)conn_list->list.array[i]; + _F1_EQ_CHECK_LONG(it_res->id, F1AP_ProtocolIE_ID_id_UE_associatedLogicalF1_ConnectionItem); + _F1_EQ_CHECK_INT(it_res->value.present, F1AP_UE_associatedLogicalF1_ConnectionItemResAck__value_PR_UE_associatedLogicalF1_ConnectionItem); + out->ue_to_reset[i] = decode_f1ap_ue_to_reset(&it_res->value.choice.UE_associatedLogicalF1_ConnectionItem); + } + } + break; + default: + AssertError(true, return false, "Reset Acknowledge: ProtocolIE id %ld not implemented, ignoring IE\n", ie->id); + break; + } + } + return true; +} + +void free_f1ap_reset_ack(f1ap_reset_ack_t *msg) +{ + for (int i = 0; i < msg->num_ue_to_reset; ++i) + free_f1ap_ue_to_reset(&msg->ue_to_reset[i]); + free(msg->ue_to_reset); +} + +bool eq_f1ap_reset_ack(const f1ap_reset_ack_t *a, const f1ap_reset_ack_t *b) +{ + _F1_EQ_CHECK_LONG(a->transaction_id, b->transaction_id); + _F1_EQ_CHECK_INT(a->num_ue_to_reset, b->num_ue_to_reset); + for (int i = 0; i < a->num_ue_to_reset; ++i) { + if (!eq_f1ap_ue_to_reset(&a->ue_to_reset[i], &b->ue_to_reset[i])) + return false; + } + return true; +} + +f1ap_reset_ack_t cp_f1ap_reset_ack(const f1ap_reset_ack_t *orig) +{ + f1ap_reset_ack_t cp = {.transaction_id = orig->transaction_id, .num_ue_to_reset = orig->num_ue_to_reset}; + if (cp.num_ue_to_reset > 0) { + cp.ue_to_reset = calloc_or_fail(cp.num_ue_to_reset, sizeof(*cp.ue_to_reset)); + for (int i = 0; i < cp.num_ue_to_reset; ++i) + cp.ue_to_reset[i] = cp_f1ap_ue_to_reset(&orig->ue_to_reset[i]); + } + return cp; +} + static const int nrb_lut[29] = {11, 18, 24, 25, 31, 32, 38, 51, 52, 65, 66, 78, 79, 93, 106, 107, 121, 132, 133, 135, 160, 162, 189, 216, 217, 245, 264, 270, 273}; diff --git a/openair2/F1AP/lib/f1ap_interface_management.h b/openair2/F1AP/lib/f1ap_interface_management.h index b3769eabfe..1b484f3476 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.h +++ b/openair2/F1AP/lib/f1ap_interface_management.h @@ -34,6 +34,13 @@ void free_f1ap_reset(f1ap_reset_t *msg); bool eq_f1ap_reset(const f1ap_reset_t *a, const f1ap_reset_t *b); f1ap_reset_t cp_f1ap_reset(const f1ap_reset_t *orig); +/* F1 Reset Ack */ +struct F1AP_F1AP_PDU *encode_f1ap_reset_ack(const f1ap_reset_ack_t *msg); +bool decode_f1ap_reset_ack(const struct F1AP_F1AP_PDU *pdu, f1ap_reset_ack_t *out); +void free_f1ap_reset_ack(f1ap_reset_ack_t *msg); +bool eq_f1ap_reset_ack(const f1ap_reset_ack_t *a, const f1ap_reset_ack_t *b); +f1ap_reset_ack_t cp_f1ap_reset_ack(const f1ap_reset_ack_t *orig); + struct F1AP_F1AP_PDU *encode_f1ap_setup_request(const f1ap_setup_req_t *msg); bool decode_f1ap_setup_request(const struct F1AP_F1AP_PDU *pdu, f1ap_setup_req_t *out); f1ap_setup_req_t cp_f1ap_setup_request(const f1ap_setup_req_t *msg); diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index 2e516a5607..29d4b625ed 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -447,6 +447,50 @@ static void test_f1ap_reset_part(void) free_f1ap_reset(&orig); } +/** + * @brief Test F1 reset ack with differently ack'd UEs ("generic ack" is + * special case with no individual UE acked) + */ +static void test_f1ap_reset_ack(void) +{ + f1ap_reset_ack_t orig = { + .transaction_id = 4, + .num_ue_to_reset = 4, + }; + orig.ue_to_reset = calloc_or_fail(orig.num_ue_to_reset, sizeof(*orig.ue_to_reset)); + orig.ue_to_reset[0].gNB_CU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[0].gNB_CU_ue_id)); + *orig.ue_to_reset[0].gNB_CU_ue_id = 10; + orig.ue_to_reset[1].gNB_DU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[1].gNB_DU_ue_id)); + *orig.ue_to_reset[1].gNB_DU_ue_id = 11; + orig.ue_to_reset[2].gNB_CU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[2].gNB_CU_ue_id)); + *orig.ue_to_reset[2].gNB_CU_ue_id = 12; + orig.ue_to_reset[2].gNB_DU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[2].gNB_DU_ue_id)); + *orig.ue_to_reset[2].gNB_DU_ue_id = 13; + // orig.ue_to_reset[3] intentionally empty (because it is allowed, both IDs are optional) + + F1AP_F1AP_PDU_t *f1enc = encode_f1ap_reset_ack(&orig); + F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); + f1ap_msg_free(f1enc); + + f1ap_reset_ack_t decoded = {0}; + bool ret = decode_f1ap_reset_ack(f1dec, &decoded); + AssertFatal(ret, "decode_f1ap_reset_ack(): could not decode message\n"); + f1ap_msg_free(f1dec); + + ret = eq_f1ap_reset_ack(&orig, &decoded); + AssertFatal(ret, "eq_f1ap_reset_ack(): decoded message doesn't match original\n"); + free_f1ap_reset_ack(&decoded); + + f1ap_reset_ack_t cp = cp_f1ap_reset_ack(&orig); + ret = eq_f1ap_reset_ack(&orig, &cp); + AssertFatal(ret, "eq_f1ap_reset_ack(): copied message doesn't match original\n"); + free_f1ap_reset_ack(&cp); + + printf("f1ap_reset_ack successful\n"); + + free_f1ap_reset_ack(&orig); +} + /** * @brief Test F1 gNB-DU Configuration Update */ @@ -671,6 +715,7 @@ int main() test_f1ap_setup_failure(); test_f1ap_reset_all(); test_f1ap_reset_part(); + test_f1ap_reset_ack(); test_f1ap_du_configuration_update(); test_f1ap_cu_configuration_update(); test_f1ap_cu_configuration_update_acknowledge(); From ff106c41a38d74de8db426833bdfee208d5a6584 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Feb 2025 15:59:10 +0100 Subject: [PATCH 40/48] Use (CU-initiated) F1 Reset Acknowledgement in stack --- openair2/F1AP/f1ap_cu_interface_management.c | 16 +++++++- openair2/F1AP/f1ap_du_interface_management.c | 38 ++++++------------- openair2/F1AP/f1ap_du_task.c | 2 + .../LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c | 5 +-- .../LAYER2/NR_MAC_gNB/mac_rrc_ul_direct.c | 6 ++- openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c | 3 +- openair2/RRC/NR/rrc_gNB.c | 5 +++ 7 files changed, 40 insertions(+), 35 deletions(-) diff --git a/openair2/F1AP/f1ap_cu_interface_management.c b/openair2/F1AP/f1ap_cu_interface_management.c index 8a6fd6311d..e697649fb5 100644 --- a/openair2/F1AP/f1ap_cu_interface_management.c +++ b/openair2/F1AP/f1ap_cu_interface_management.c @@ -39,7 +39,21 @@ int CU_handle_RESET_ACKNOWLEDGE(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu) { - AssertFatal(1 == 0, "Not implemented yet\n"); + DevAssert(pdu != NULL); + + f1ap_reset_ack_t ack = {0}; + /* Decode */ + if (!decode_f1ap_reset_ack(pdu, &ack)) { + LOG_E(F1AP, "cannot decode F1AP Reset acknowledgement\n"); + free_f1ap_reset_ack(&ack); + return -1; + } + /* Send to RRC (ITTI) */ + MessageDef *message_p = itti_alloc_new_message(TASK_CU_F1, 0, F1AP_RESET_ACK); + message_p->ittiMsgHeader.originInstance = assoc_id; + F1AP_RESET_ACK(message_p) = ack; /* "move" message into ITTI, RRC thread will free it */ + itti_send_msg_to_task(TASK_RRC_GNB, GNB_MODULE_ID_TO_INSTANCE(instance), message_p); + return 0; } int CU_send_RESET_ACKNOWLEDGE(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack) diff --git a/openair2/F1AP/f1ap_du_interface_management.c b/openair2/F1AP/f1ap_du_interface_management.c index 0d0c9c2946..7a892086b8 100644 --- a/openair2/F1AP/f1ap_du_interface_management.c +++ b/openair2/F1AP/f1ap_du_interface_management.c @@ -117,35 +117,21 @@ int DU_handle_RESET(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream, int DU_send_RESET_ACKNOWLEDGE(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack) { - F1AP_F1AP_PDU_t pdu= {0}; - uint8_t *buffer; - uint32_t len; - /* Create */ - /* 0. pdu Type */ - pdu.present = F1AP_F1AP_PDU_PR_successfulOutcome; - asn1cCalloc(pdu.choice.successfulOutcome, successMsg); - successMsg->procedureCode = F1AP_ProcedureCode_id_Reset; - successMsg->criticality = F1AP_Criticality_reject; - successMsg->value.present = F1AP_SuccessfulOutcome__value_PR_ResetAcknowledge; - F1AP_ResetAcknowledge_t *f1ResetAcknowledge = &successMsg->value.choice.ResetAcknowledge; - /* mandatory */ - /* c1. Transaction ID (integer value) */ - asn1cSequenceAdd(f1ResetAcknowledge->protocolIEs.list, F1AP_ResetAcknowledgeIEs_t, ieC1); - ieC1->id = F1AP_ProtocolIE_ID_id_TransactionID; - ieC1->criticality = F1AP_Criticality_reject; - ieC1->value.present = F1AP_ResetAcknowledgeIEs__value_PR_TransactionID; - ieC1->value.choice.TransactionID = ack->transaction_id; - - /* TODO: (Optional) partialF1Interface, criticality diagnostics */ - - /* encode */ - if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) { - LOG_E(F1AP, "Failed to encode F1ResetAcknowledge\n"); + F1AP_F1AP_PDU_t *pdu = encode_f1ap_reset_ack(ack); + if (!pdu) { + LOG_E(F1AP, "failed to create ASN.1 message for reset acknowledge\n"); return -1; } - /* send */ - ASN_STRUCT_RESET(asn_DEF_F1AP_F1AP_PDU, &pdu); + /* encode */ + uint8_t *buffer; + uint32_t len; + int encoded = f1ap_encode_pdu(pdu, &buffer, &len); + ASN_STRUCT_FREE(asn_DEF_F1AP_F1AP_PDU, pdu); + if (encoded <= 0) { + LOG_E(F1AP, "Failed to encode F1ResetAcknowledge\n"); + return -1; + } f1ap_itti_send_sctp_data_req(assoc_id, buffer, len); return 0; } diff --git a/openair2/F1AP/f1ap_du_task.c b/openair2/F1AP/f1ap_du_task.c index 0d675a76e1..c231561eec 100644 --- a/openair2/F1AP/f1ap_du_task.c +++ b/openair2/F1AP/f1ap_du_task.c @@ -35,6 +35,7 @@ #include "f1ap_du_ue_context_management.h" #include "f1ap_du_rrc_message_transfer.h" #include "lib/f1ap_rrc_message_transfer.h" +#include "lib/f1ap_interface_management.h" #include "f1ap_du_task.h" #include @@ -150,6 +151,7 @@ void *F1AP_DU_task(void *arg) { case F1AP_RESET_ACK: DU_send_RESET_ACKNOWLEDGE(assoc_id, &F1AP_RESET_ACK(msg)); + free_f1ap_reset_ack(&F1AP_RESET_ACK(msg)); break; case F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE: diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index 124d63d293..f92559c4ee 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -125,12 +125,9 @@ void f1_reset_cu_initiated(const f1ap_reset_t *reset) { LOG_I(MAC, "F1 Reset initiated by CU\n"); - f1ap_reset_ack_t ack = {0}; + f1ap_reset_ack_t ack = {.transaction_id = reset->transaction_id}; if(reset->reset_type == F1AP_RESET_ALL) { du_clear_all_ue_states(); - ack = (f1ap_reset_ack_t) { - .transaction_id = reset->transaction_id - }; } else { // reset->reset_type == F1AP_RESET_PART_OF_F1_INTERFACE AssertFatal(1==0, "Not implemented yet\n"); diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_direct.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_direct.c index ad8978d514..e687991a5e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_direct.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_direct.c @@ -34,8 +34,10 @@ static void f1_reset_du_initiated_direct(const f1ap_reset_t *reset) static void f1_reset_acknowledge_cu_initiated_direct(const f1ap_reset_ack_t *ack) { - (void) ack; - AssertFatal(false, "%s() not implemented yet\n", __func__); + MessageDef *msg = itti_alloc_new_message(TASK_MAC_GNB, 0, F1AP_RESET_ACK); + msg->ittiMsgHeader.originInstance = -1; // means monolithic + F1AP_RESET_ACK(msg) = cp_f1ap_reset_ack(ack); + itti_send_msg_to_task(TASK_RRC_GNB, 0, msg); } static void f1_setup_request_direct(const f1ap_setup_req_t *req) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c index 544ab3889c..81373e0bc9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_ul_f1ap.c @@ -61,8 +61,7 @@ static void f1_reset_du_initiated_f1ap(const f1ap_reset_t *reset) static void f1_reset_acknowledge_cu_initiated_f1ap(const f1ap_reset_ack_t *ack) { MessageDef *msg = itti_alloc_new_message(TASK_MAC_GNB, 0, F1AP_RESET_ACK); - f1ap_reset_ack_t *f1ap_msg = &F1AP_RESET_ACK(msg); - *f1ap_msg = *ack; + F1AP_RESET_ACK(msg) = cp_f1ap_reset_ack(ack); itti_send_msg_to_task(TASK_DU_F1, 0, msg); } diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index fd69a4fafd..e8d65e5731 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2607,6 +2607,11 @@ void *rrc_gnb_task(void *args_p) { LOG_E(NR_RRC, "Handling of F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE not implemented\n"); break; + case F1AP_RESET_ACK: + LOG_I(NR_RRC, "received F1AP reset acknowledgement\n"); + free_f1ap_reset_ack(&F1AP_RESET_ACK(msg_p)); + break; + /* Messages from X2AP */ case X2AP_ENDC_SGNB_ADDITION_REQ: LOG_I(NR_RRC, "Received ENDC sgNB addition request from X2AP \n"); From d2bb779e782f8f4a97759f145ad519d001e2ed3e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Feb 2025 17:14:13 +0100 Subject: [PATCH 41/48] Refactor rrc_remove_ue() to not send NGAP ctxt release cplt The next commit will use rrc_remove_ue() to delete a CU(-CP+-UP) context. However, we don't send NGAP UE context release at that moment (we will instead *request* the release); more generally, it's not clear that every user of rrc_remove_ue() wants to actually send this message. Hence, refactor the code by separately calling rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE() where appropriate. --- openair2/RRC/NR/rrc_gNB.c | 7 +++---- openair2/RRC/NR/rrc_gNB_NGAP.c | 3 +++ openair2/RRC/NR/rrc_gNB_radio_bearers.c | 6 ++++++ openair2/RRC/NR/rrc_gNB_radio_bearers.h | 3 +++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index e8d65e5731..358b5af653 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2074,10 +2074,6 @@ void rrc_remove_ue(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *ue_context_p) * are in E1, we also need to free the UE in the CU-CP, so call it twice to * cover all cases */ nr_pdcp_remove_UE(UE->rrc_ue_id); - uint32_t pdu_sessions[256]; - for (int i = 0; i < UE->nb_of_pdusessions && i < 256; ++i) - pdu_sessions[i] = UE->pduSession[i].param.pdusession_id; - rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions); LOG_I(NR_RRC, "removed UE CU UE ID %u/RNTI %04x \n", UE->rrc_ue_id, UE->rnti); rrc_delete_ue_data(UE); rrc_gNB_remove_ue_context(rrc, ue_context_p); @@ -2099,6 +2095,9 @@ static void rrc_CU_process_ue_context_release_complete(MessageDef *msg_p) /* only trigger release if it has been requested by core * otherwise, it might be CU that requested release on a DU during normal * operation (i.e, handover) */ + uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION]; + get_pduSession_array(UE, pdu_sessions); + rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions); rrc_remove_ue(RC.nrrrc[0], ue_context_p); } } diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 0b1c3012f4..12ef7ec2f4 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1231,6 +1231,9 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_ /* UE will be freed after UE context release complete */ } else { // the DU is offline already + uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION]; + get_pduSession_array(UE, pdu_sessions); + rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions); rrc_remove_ue(rrc, ue_context_p); } diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index ce6eb40bbe..8255ccdb38 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -58,6 +58,12 @@ rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id return find_pduSession(ue, id, false); } +void get_pduSession_array(gNB_RRC_UE_t *ue, uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION]) +{ + for (int i = 0; i < ue->nb_of_pdusessions && i < NGAP_MAX_PDU_SESSION; ++i) + pdu_sessions[i] = ue->pduSession[i].param.pdusession_id; +} + drb_t *get_drb(gNB_RRC_UE_t *ue, uint8_t drb_id) { DevAssert(drb_id > 0 && drb_id <= 32); diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h index 37ddbdd431..133e17627f 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -73,6 +73,9 @@ rrc_pdu_session_param_t *find_pduSession(gNB_RRC_UE_t *ue, int id, bool create); /// @brief get PDU session of UE ue through the DRB drb_id rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id); +/// @brief get the PDU sessions of this UE in a single array +void get_pduSession_array(gNB_RRC_UE_t *ue, uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION]); + /// @brief set PDCP configuration in a bearer context management message void set_bearer_context_pdcp_config(bearer_context_pdcp_config_t *pdcp_config, drb_t *rrc_drb, bool um_on_default_drb); From e8661f5ae5d51cf1ab07ad98eef2b563f8fedc39 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sat, 8 Feb 2025 13:06:30 +0100 Subject: [PATCH 42/48] Reset CU-CP E1 UE context after E1 Setup Response As in deba3ae066df ("Reset CU-UP E1 UE contexts after E1 Setup Response"), the CU-CP should reset the E1 UE contexts after E1 Setup Response. 38.463 sec. 8.2.3 says > This procedure also re-initialises the E1AP UE-related contexts (if > any) and erases all related signalling connections in the two nodes like > a Reset procedure would do. In the case of CU-CP, this is a bit more complicated. More precisely, we need to wait for the CU-UP to be available again before informing the DU about the loss (implemented via an F1 Reset message), because otherwise, the UE might reconnect immediately, before the CU-UP is online again. Hence, on connection loss, we mark affected UEs (by setting their E1 association ID to 0); as long as the CU-UP does not connect, nothing will happen (but UEs don't have user plane). Upon CU-UP connecting again, we request release of the UEs via NGAP (similarly to what is done if the DU goes offline, see invalidate_du_connections()), free the UE contexts locally, and send an F1 Reset message to the DU, because 38.473 sec 8.2.1.2.1 says > a failure at the gNB-CU [...] has resulted in the loss of some or > all transaction reference information [...] a RESET message shall be > sent to the gNB-DU. The core should accept and send an NGAP release command message; since no UE contexts are to be found (already deleted), the CU-CP will just send a release complete (together with some warnings). It might be possible to more gracefully handle the situation (e.g., as described in 38.401 section 8.9.5 "Change of gNB-CU-UP"), but - we lack the PDCP SN exchange - the current procedure has the advantage of being equivalent to "restart of the entire gNB", with less downtown. --- openair2/RRC/NR/rrc_gNB_cuup.c | 91 +++++++++++++++++++++++++++++++++- openair2/RRC/NR/rrc_gNB_du.c | 11 ++++ openair2/RRC/NR/rrc_gNB_du.h | 2 + 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR/rrc_gNB_cuup.c b/openair2/RRC/NR/rrc_gNB_cuup.c index 689670fa85..c065299824 100644 --- a/openair2/RRC/NR/rrc_gNB_cuup.c +++ b/openair2/RRC/NR/rrc_gNB_cuup.c @@ -24,13 +24,17 @@ #include #include #include -#include "PHY/defs_common.h" + +#include "common/utils/alg/find.h" +#include "common/utils/alg/foreach.h" #include "RRC/NR/nr_rrc_proto.h" #include "T.h" #include "as_message.h" #include "assertions.h" #include "common/ran_context.h" #include "common/utils/T/T.h" +#include "rrc_gNB_UE_context.h" +#include "rrc_gNB_du.h" #include "e1ap_messages_types.h" #include "intertask_interface.h" #include "nr_rrc_defs.h" @@ -38,6 +42,7 @@ #include "rrc_messages_types.h" #include "tree.h" #include "e1ap_interface_management.h" +#include "rrc_gNB_NGAP.h" static int cuup_compare(const nr_rrc_cuup_container_t *a, const nr_rrc_cuup_container_t *b) { @@ -168,6 +173,69 @@ static void e1ap_setup_failure(sctp_assoc_t assoc_id, uint64_t transac_id, e1ap_ itti_send_msg_to_task(TASK_CUCP_E1, 0 /*unused by callee*/, msg_p); } +static bool has_assoc_id(const void *vval, const void *vit) +{ + sctp_assoc_t val = *(sctp_assoc_t *)vval; + sctp_assoc_t it = *(sctp_assoc_t *)vit; + return val == it; +} + +/* @brief Remove RRC UE contexts of UE without E1 connection. + * + * 38.463 sec 8.2.3. says that "[E1 Setup] procedure also re-initialises the + * E1AP UE-related contexts (if any) and erases all related signalling + * connections in the two nodes like a Reset procedure would do.". If the CU-UP + * reconnects, it's previous UE contexts are here, but not associated. To + * fulfil the above, we simply free all UE contexts that don't have a CU-UP, + * and send a F1 Reset message to the corresponding DU(s), following the fact + * that (38.473 sec 8.2.1.2.1) "a failure at the gNB-CU [...] has resulted in + * the loss of some or all transaction reference information[...] a RESET + * message shall be sent to the gNB-DU.". This might be overarching in that UEs + * that are in the process of connecting or that are on another DU might be + * affected by that reset procedure as well; we suppose they will connect later + * again. */ +static void remove_unassociated_e1_connections(gNB_RRC_INST *rrc, sctp_assoc_t e1_assoc_id) +{ + seq_arr_t affected_du; + seq_arr_init(&affected_du, sizeof(sctp_assoc_t)); + seq_arr_t ue_context_to_remove; + seq_arr_init(&ue_context_to_remove, sizeof(rrc_gNB_ue_context_t *)); + + rrc_gNB_ue_context_t *ue_context_p = NULL; + RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc->rrc_ue_head) { + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + uint32_t ue_id = UE->rrc_ue_id; + if (ue_associated_to_cuup(rrc, UE)) + continue; + f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id); + elm_arr_t ret = find_if(&affected_du, &ue_data.du_assoc_id, has_assoc_id); + if (!ret.found) + seq_arr_push_back(&affected_du, &ue_data.du_assoc_id, sizeof(ue_data.du_assoc_id)); + + /* remove UE context: we store the pointer, so pass pointer to ue_context_p */ + seq_arr_push_back(&ue_context_to_remove, &ue_context_p, sizeof(ue_context_p)); + LOG_I(NR_RRC, "remove E1-unassociated UE context ID %d\n", ue_id); + } + + for (int i = 0; i < seq_arr_size(&ue_context_to_remove); ++i) { + /* we retrieve a pointer (=iterator) to the UE context pointer + * (ue_context_p), so dereference once */ + rrc_gNB_ue_context_t *p = *(rrc_gNB_ue_context_t **)seq_arr_at(&ue_context_to_remove, i); + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_RADIO_CONNECTION_WITH_UE_LOST}; + rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(0, p, cause); + rrc_remove_ue(rrc, p); + } + seq_arr_free(&ue_context_to_remove, NULL); + + for (int i = 0; i < seq_arr_size(&affected_du); ++i) { + void *it = seq_arr_at(&affected_du, i); + sctp_assoc_t du_assoc_id = *(sctp_assoc_t *)it; + LOG_W(NR_RRC, "trigger F1 reset on DU %d due to E1 connection loss\n", du_assoc_id); + trigger_f1_reset(rrc, du_assoc_id); + } + seq_arr_free(&affected_du, NULL); +} + /** * @brief E1AP Setup Request processing on CU-CP */ @@ -216,6 +284,8 @@ int rrc_gNB_process_e1_setup_req(sctp_assoc_t assoc_id, const e1ap_setup_req_t * RB_INSERT(rrc_cuup_tree, &rrc->cuups, cuup); rrc->num_cuups++; + remove_unassociated_e1_connections(rrc, assoc_id); + MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, E1AP_SETUP_RESP); msg_p->ittiMsgHeader.originInstance = assoc_id; e1ap_setup_resp_t *resp = &E1AP_SETUP_RESP(msg_p); @@ -225,6 +295,24 @@ int rrc_gNB_process_e1_setup_req(sctp_assoc_t assoc_id, const e1ap_setup_req_t * return 0; } +/* @brief invalidate (remove) all E1 assoc ID from UE contexts belonging to this CU-UP */ +static void invalidate_cuup_connections(gNB_RRC_INST *rrc, sctp_assoc_t e1_assoc_id) +{ + rrc_gNB_ue_context_t *ue_context_p = NULL; + RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc->rrc_ue_head) { + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + uint32_t ue_id = UE->rrc_ue_id; + f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id); + if (ue_data.e1_assoc_id != e1_assoc_id) + continue; /* UE is on another CU-UP */ + + ue_data.e1_assoc_id = 0; + bool success = cu_update_f1_ue_data(ue_id, &ue_data); + DevAssert(success); + LOG_W(NR_RRC, "CU-UP %d of UE %d is gone\n", e1_assoc_id, ue_id); + } +} + /** * @brief RRC Processing of the indication of E1 connection loss on CU-CP */ @@ -232,6 +320,7 @@ void rrc_gNB_process_e1_lost_connection(gNB_RRC_INST *rrc, e1ap_lost_connection_ { LOG_I(NR_RRC, "Received E1 connection loss indication on RRC\n"); AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n"); + invalidate_cuup_connections(rrc, assoc_id); nr_rrc_cuup_container_t e = {.assoc_id = assoc_id}; nr_rrc_cuup_container_t *cuup = RB_FIND(rrc_cuup_tree, &rrc->cuups, &e); if (cuup == NULL) { diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 802735ee85..402e245dac 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -644,3 +644,14 @@ nr_rrc_du_container_t *find_target_du(gNB_RRC_INST *rrc, sctp_assoc_t source_ass } return target_du; } + +void trigger_f1_reset(gNB_RRC_INST *rrc, sctp_assoc_t du_assoc_id) +{ + f1ap_reset_t reset = { + .transaction_id = F1AP_get_next_transaction_identifier(0, 0), + .cause = F1AP_CAUSE_TRANSPORT, + .cause_value = 1, // F1AP_CauseTransport_transport_resource_unavailable + .reset_type = F1AP_RESET_ALL, // DU does not support partial reset yet + }; + rrc->mac_rrc.f1_reset(du_assoc_id, &reset); +} diff --git a/openair2/RRC/NR/rrc_gNB_du.h b/openair2/RRC/NR/rrc_gNB_du.h index aadac0845a..514416211f 100644 --- a/openair2/RRC/NR/rrc_gNB_du.h +++ b/openair2/RRC/NR/rrc_gNB_du.h @@ -60,4 +60,6 @@ struct nr_rrc_du_container_t *find_target_du(struct gNB_RRC_INST_s *rrc, sctp_as } \ } +void trigger_f1_reset(struct gNB_RRC_INST_s *rrc, sctp_assoc_t du_assoc_id); + #endif /* RRC_GNB_DU_H_ */ From 627f8171ec6b963eea4ca1727d1f12346de1f886 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 11 Feb 2025 14:04:19 +0100 Subject: [PATCH 43/48] Add test for Bearer Context Modification Request --- openair2/E1AP/tests/e1ap_lib_test.c | 116 ++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/openair2/E1AP/tests/e1ap_lib_test.c b/openair2/E1AP/tests/e1ap_lib_test.c index 368698a517..59e6f08e66 100644 --- a/openair2/E1AP/tests/e1ap_lib_test.c +++ b/openair2/E1AP/tests/e1ap_lib_test.c @@ -339,6 +339,120 @@ static void test_e1_cuup_setup_failure(void) free_e1ap_cuup_setup_failure(&orig); } +/** + * @brief Test E1AP Bearer Context Modification Request encoding/decoding + */ +static void test_bearer_context_modification_request(void) +{ + const bearer_context_sdap_config_t dummy_sdap_config = { + .defaultDRB = 0, + .sDAP_Header_DL = false, + .sDAP_Header_UL = false, + }; + + const bearer_context_pdcp_config_t dummy_pdcp_config = { + .discardTimer = E1AP_DiscardTimer_ms100, + .pDCP_Reestablishment = true, + .pDCP_SN_Size_DL = E1AP_PDCP_SN_Size_s_12, + .pDCP_SN_Size_UL = E1AP_PDCP_SN_Size_s_12, + .reorderingTimer = 5, + .rLC_Mode = E1AP_RLC_Mode_rlc_um_bidirectional, + }; + + const qos_flow_to_setup_t dummy_qos_flows = { + .qfi = 9, + .qos_params.alloc_reten_priority.preemption_capability = E1AP_Pre_emptionCapability_may_trigger_pre_emption, + .qos_params.alloc_reten_priority.preemption_vulnerability = E1AP_Pre_emptionVulnerability_pre_emptable, + .qos_params.alloc_reten_priority.priority_level = E1AP_PriorityLevel_no_priority, + .qos_params.qos_characteristics.non_dynamic.fiveqi = 9, + }; + + DRB_nGRAN_to_mod_t drb_to_mod = { + .numDlUpParam = 1, + .DlUpParamList[0].cell_group_id = MCG, + .DlUpParamList[0].tl_info = create_up_tl_info(), + .id = 1, + .pdcp_config = malloc_or_fail(sizeof(*drb_to_mod.pdcp_config)), + .pdcp_sn_status_requested = true, + .numQosFlow2Setup = 1, + .qosFlows[0] = dummy_qos_flows, + }; + *drb_to_mod.pdcp_config = dummy_pdcp_config; + + pdu_session_to_mod_t pdusession_mod_item = { + .sessionId = 1, + .numDRB2Modify = 1, + .DRBnGRanModList[0] = drb_to_mod, + }; + + DRB_nGRAN_to_setup_t drb_to_setup = { + .id = 1, + .cellGroupList[0] = MCG, + .numCellGroups = 1, + .pdcp_config = dummy_pdcp_config, + .sdap_config = dummy_sdap_config, + .numQosFlow2Setup = 1, + .qosFlows[0] = dummy_qos_flows, + .drb_inactivity_timer = malloc_or_fail(sizeof(*drb_to_setup.drb_inactivity_timer)), + }; + *drb_to_setup.drb_inactivity_timer = 500; + + pdu_session_to_setup_t pdusession_setup_item = { + .numDRB2Setup = 1, + .nssai.sd = 0x01, + .nssai.sst = 0x01, + .UP_TL_information.teId = 0x12345, + .UP_TL_information.tlAddress = 167772161, + .DRBnGRanList[0] = drb_to_setup, + }; + + // Initialize the Bearer Context Modification Request + e1ap_bearer_mod_req_t orig = { + .gNB_cu_cp_ue_id = 0x1234, + .gNB_cu_up_ue_id = 0x5678, + .bearerContextStatus = malloc_or_fail(sizeof(*orig.bearerContextStatus)), + .inactivityTimer = malloc_or_fail(sizeof(*orig.inactivityTimer)), + .numPDUSessions = 1, + .pduSession[0] = pdusession_setup_item, + .numPDUSessionsMod = 1, + .pduSessionMod[0] = pdusession_mod_item, + }; + *orig.bearerContextStatus = BEARER_SUSPEND; + *orig.inactivityTimer = 1000; + + // Encode the original message + E1AP_E1AP_PDU_t *enc = encode_E1_bearer_context_mod_request(&orig); + + // Decode the encoded message + E1AP_E1AP_PDU_t *dec = e1ap_encode_decode(enc); + + // Free the encoded message + e1ap_msg_free(enc); + + // Decode the message into a new struct + e1ap_bearer_mod_req_t decoded = {0}; + AssertFatal(decode_E1_bearer_context_mod_request(dec, &decoded), "decode_E1_bearer_context_mod_request(): could not decode message\n"); + + // Free the decoded E1AP message + e1ap_msg_free(dec); + + // Compare the original and decoded structs + AssertFatal(eq_bearer_context_mod_request(&orig, &decoded), "eq_bearer_context_mod_request(): decoded message doesn't match\n"); + + // Free the memory for the decoded message + free_e1ap_context_mod_request(&decoded); + + // Deep copy the original message + e1ap_bearer_mod_req_t cp = cp_bearer_context_mod_request(&orig); + + // Verify the deep copy matches the original + AssertFatal(eq_bearer_context_mod_request(&orig, &cp), "eq_bearer_context_mod_request(): copied message doesn't match\n"); + + // Free the copied and original message + free_e1ap_context_mod_request(&cp); + free_e1ap_context_mod_request(&orig); +} + int main() { // E1 Bearer Context Setup @@ -348,5 +462,7 @@ int main() test_e1_cuup_setup_request(); test_e1_cuup_setup_response(); test_e1_cuup_setup_failure(); + // E1 Bearer Context Modification Request + test_bearer_context_modification_request(); return 0; } From a1eb8f35fbbf0d2959d49c7c683607cc1adad22e Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 22 Jan 2025 16:00:34 +0100 Subject: [PATCH 44/48] bugfix: use correct algorithm and derive keys accordingly This was not a big problem, but we were advertising wrong algorithm and deriving keys using this wrong algorithm in case DRB ciphering and/or integrity was not active. (Say SRBs are configured with NIA2 but DRBs are configured without integrity, we would advertise NIA2 and send the integrity key derived for NIA2, instead of NIA0 for both.) It was not a problem because in the PDU Session Resource To Setup item, there is "securityIndication" which we use to effectively activate/deactivate the ciphering and/or integrity. But it did not look clean to see a SecurityInformation with incorrect data when inspecting the message in wireshark. So let's use the correct values. We could also not include the SecurityInformation if both ciphering and integrity are not used, and only include ciphering if integrity is not used (because integrity settings are optional). But then if only integrity is used, we still need to include ciphering, setting algorithm to NEA0 (no ciphering), because the ciphering settings are always included. This logic is too complex, let's use the simple one to always include SecurityInformation with NEA0 and/or NIA0 if ciphering and/or integrity is not activated. --- openair2/RRC/NR/rrc_gNB_NGAP.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 7f17baea4a..87ee2623eb 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -356,10 +356,10 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession decodePDUSessionResourceSetup(session); bearer_req.gNB_cu_cp_ue_id = UE->rrc_ue_id; security_information_t *secInfo = &bearer_req.secInfo; - secInfo->cipheringAlgorithm = UE->ciphering_algorithm; - secInfo->integrityProtectionAlgorithm = UE->integrity_algorithm; - nr_derive_key(UP_ENC_ALG, UE->ciphering_algorithm, UE->kgnb, (uint8_t *)secInfo->encryptionKey); - nr_derive_key(UP_INT_ALG, UE->integrity_algorithm, UE->kgnb, (uint8_t *)secInfo->integrityProtectionKey); + secInfo->cipheringAlgorithm = rrc->security.do_drb_ciphering ? UE->ciphering_algorithm : 0; + secInfo->integrityProtectionAlgorithm = rrc->security.do_drb_integrity ? UE->integrity_algorithm : 0; + nr_derive_key(UP_ENC_ALG, secInfo->cipheringAlgorithm, UE->kgnb, (uint8_t *)secInfo->encryptionKey); + nr_derive_key(UP_INT_ALG, secInfo->integrityProtectionAlgorithm, UE->kgnb, (uint8_t *)secInfo->integrityProtectionKey); bearer_req.ueDlAggMaxBitRate = ueAggMaxBitRateDownlink; pdu_session_to_setup_t *pdu = bearer_req.pduSession + bearer_req.numPDUSessions; bearer_req.numPDUSessions++; From 182b05bd5c6493ec5d7b2ac40ed59ac33bcc4142 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 22 Jan 2025 16:06:36 +0100 Subject: [PATCH 45/48] e1: security information is optional in bearer modification Check if the IE is memory allocated to deal with presence/absence of this information element in Bearer Context Modification Request and add encoding/decoding in the code. Note that for decoding the integrity settings may be absent, in which case we consider NIA0 to be used (no integrity protection). To be refined if needed. And for encoding, we always encode integrity settings, even if it's NIA0. May also be refined if needed. --- openair2/E1AP/e1ap.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/openair2/E1AP/e1ap.c b/openair2/E1AP/e1ap.c index 72eff658cb..c17f54be11 100644 --- a/openair2/E1AP/e1ap.c +++ b/openair2/E1AP/e1ap.c @@ -451,6 +451,21 @@ static int fill_BEARER_CONTEXT_MODIFICATION_REQUEST(e1ap_bearer_mod_req_t *const ieC2->criticality = E1AP_Criticality_reject; ieC2->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_UP_UE_E1AP_ID; ieC2->value.choice.GNB_CU_UP_UE_E1AP_ID = bearerCxt->gNB_cu_up_ue_id; + /* optional */ + /* Security Information */ + if (bearerCxt->secInfo) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ie); + ie->id = E1AP_ProtocolIE_ID_id_SecurityInformation; + ie->criticality = E1AP_Criticality_reject; + ie->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_SecurityInformation; + E1AP_SecurityAlgorithm_t *securityAlgorithm = &ie->value.choice.SecurityInformation.securityAlgorithm; + E1AP_UPSecuritykey_t *uPSecuritykey = &ie->value.choice.SecurityInformation.uPSecuritykey; + securityAlgorithm->cipheringAlgorithm = bearerCxt->secInfo->cipheringAlgorithm; + OCTET_STRING_fromBuf(&uPSecuritykey->encryptionKey, bearerCxt->secInfo->encryptionKey, E1AP_SECURITY_KEY_SIZE); + asn1cCallocOne(securityAlgorithm->integrityProtectionAlgorithm, bearerCxt->secInfo->integrityProtectionAlgorithm); + asn1cCalloc(uPSecuritykey->integrityProtectionKey, protKey); + OCTET_STRING_fromBuf(protKey, bearerCxt->secInfo->integrityProtectionKey, E1AP_SECURITY_KEY_SIZE); + } /* optional */ /* c3. E1AP_ProtocolIE_ID_id_System_BearerContextModificationRequest */ asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ieC3); @@ -606,6 +621,26 @@ static void extract_BEARER_CONTEXT_MODIFICATION_REQUEST(const E1AP_E1AP_PDU_t *p bearerCxt->gNB_cu_up_ue_id = ie->value.choice.GNB_CU_UP_UE_E1AP_ID; break; + case E1AP_ProtocolIE_ID_id_SecurityInformation: { + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextModificationRequestIEs__value_PR_SecurityInformation); + E1AP_SecurityInformation_t *sec = &ie->value.choice.SecurityInformation; + DevAssert(sec->uPSecuritykey.encryptionKey.size == E1AP_SECURITY_KEY_SIZE); + DevAssert(sec->uPSecuritykey.integrityProtectionKey == NULL + || sec->uPSecuritykey.integrityProtectionKey->size == E1AP_SECURITY_KEY_SIZE); + bearerCxt->secInfo = malloc_or_fail(sizeof(*bearerCxt->secInfo)); + bearerCxt->secInfo->cipheringAlgorithm = sec->securityAlgorithm.cipheringAlgorithm; + bearerCxt->secInfo->integrityProtectionAlgorithm = sec->securityAlgorithm.integrityProtectionAlgorithm == NULL + ? 0 + : *sec->securityAlgorithm.integrityProtectionAlgorithm; + memcpy(bearerCxt->secInfo->encryptionKey, sec->uPSecuritykey.encryptionKey.buf, E1AP_SECURITY_KEY_SIZE); + if (sec->securityAlgorithm.integrityProtectionAlgorithm == NULL) + memset(bearerCxt->secInfo->integrityProtectionKey, 0, E1AP_SECURITY_KEY_SIZE); + else + memcpy(bearerCxt->secInfo->integrityProtectionKey, sec->uPSecuritykey.integrityProtectionKey->buf, E1AP_SECURITY_KEY_SIZE); + break; + } + case E1AP_ProtocolIE_ID_id_System_BearerContextModificationRequest: DevAssert(ie->criticality == E1AP_Criticality_reject); DevAssert(ie->value.present == E1AP_BearerContextModificationRequestIEs__value_PR_System_BearerContextModificationRequest); From 1273446804ca70d0307220863d04f90bf45b4dfc Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 22 Jan 2025 16:11:00 +0100 Subject: [PATCH 46/48] e1: change security settings only if parameters present If the Bearer Context Modification Request does not contain security information, the PDCP entity shall keep the currently configured security settings. Passing -1 for ciphering_algorithm and integrity_algorithm is the way to implement this logic. --- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index ed39dbeff0..3417940051 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -279,6 +279,10 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) security_parameters.integrity_algorithm = req->secInfo->integrityProtectionAlgorithm; memcpy(security_parameters.ciphering_key, req->secInfo->encryptionKey, NR_K_KEY_SIZE); memcpy(security_parameters.integrity_key, req->secInfo->integrityProtectionKey, NR_K_KEY_SIZE); + } else { + /* don't change security settings if not present in the Bearer Context Modification Request */ + security_parameters.ciphering_algorithm = -1; + security_parameters.integrity_algorithm = -1; } nr_pdcp_reestablishment(req->gNB_cu_up_ue_id, to_modif->id, From 410446cbe9132474343cf943ae655b75cbb188bb Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 22 Jan 2025 16:15:04 +0100 Subject: [PATCH 47/48] e1: no need to pass security information since it does not change In Bearer Context Modification Request, the security information field is needed only if we change the PDCP algorithms/keys. Since we don't, no need to pass it. Let's keep the old code, maybe the current understanding of the specifications is wrong. --- openair2/RRC/NR/rrc_gNB.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 6cfb0047af..d9e83bb4c2 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -816,9 +816,17 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) /* increase DRB to modify counter */ pdu_e1->numDRB2Modify += 1; } + +#if 0 + /* According to current understanding of E1 specifications, it is not needed + * to send security information because this does not change. + * But let's keep the code here in case it's needed. + */ // Always send security information req.secInfo = malloc_or_fail(sizeof(*req.secInfo)); fill_security_info(rrc, ue_p, req.secInfo); +#endif + /* Send E1 Bearer Context Modification Request (3GPP TS 38.463) */ sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, ue_p); rrc->cucp_cuup.bearer_context_mod(assoc_id, &req); From ac6b1b189445e9c576eafa0170baef85ca553ec4 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 4 Feb 2025 12:56:33 +0100 Subject: [PATCH 48/48] Enable integrity in E1 SABOX CI test --- ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml b/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml index 6c3940d306..5a55ade3c8 100644 --- a/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml +++ b/ci-scripts/yaml_files/sa_e1_b200_gnb/docker-compose.yml @@ -9,6 +9,7 @@ services: environment: USE_ADDITIONAL_OPTIONS: --telnetsrv --telnetsrv.listenport 9090 --telnetsrv.shrmod ci --log_config.global_log_options level,nocolor,time,line_num,function + --security.drb_integrity yes volumes: - ../../conf_files/gnb-cucp.sa.f1.quectel.conf:/opt/oai-gnb/etc/gnb.conf # for performance reasons, we use host mode: in bridge mode, we have