diff --git a/ci-scripts/conf_files/gnb.sa.band66.106prb.rfsim.conf b/ci-scripts/conf_files/gnb.sa.band66.106prb.rfsim.conf index bae2bc15ff..fc957d9eef 100644 --- a/ci-scripts/conf_files/gnb.sa.band66.106prb.rfsim.conf +++ b/ci-scripts/conf_files/gnb.sa.band66.106prb.rfsim.conf @@ -18,7 +18,7 @@ gNBs = ////////// Physical parameters: min_rxtxtime = 6; - enable_sdap = 1; + enable_sdap = 0; servingCellConfigCommon = ( { diff --git a/ci-scripts/conf_files/untested/gnb.sa.band66.fr1.106PRB.usrpn300.conf b/ci-scripts/conf_files/untested/gnb.sa.band66.fr1.106PRB.usrpn300.conf index 6715202229..22a65995d5 100644 --- a/ci-scripts/conf_files/untested/gnb.sa.band66.fr1.106PRB.usrpn300.conf +++ b/ci-scripts/conf_files/untested/gnb.sa.band66.fr1.106PRB.usrpn300.conf @@ -18,7 +18,7 @@ gNBs = ////////// Physical parameters: min_rxtxtime = 6; - enable_sdap = 1; + enable_sdap = 0; servingCellConfigCommon = ( { diff --git a/doc/RRC/rrc-usage.md b/doc/RRC/rrc-usage.md index bae31e864a..baeee1480d 100644 --- a/doc/RRC/rrc-usage.md +++ b/doc/RRC/rrc-usage.md @@ -84,7 +84,7 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration. `0xffffff` is a reserved value and means "no SD" Note that: SST=1, no SD is "eMBB"; SST=2, no SD is "URLLC"; SST=3, no SD is "mMTC" -- `enable_sdap` (default: false): enable the use of the SDAP layer. If +- `enable_sdap` (default: true): enable the use of the SDAP layer. If deactivated, a transparent SDAP header is prepended to packets, but no further processing is being done. - `cu_sibs` (default: `[]`) list of SIBs to give to the DU for transmission. @@ -94,8 +94,6 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration. - `um_on_default_drb` (default: false): use RLC UM instead of RLC AM on default bearers -- `drbs` (default: 0): the number of DRBs to allocate for a UE, only useful for - do-ra or phy-test testing #### Neighbor-gNB configuration diff --git a/doc/RUNMODEM.md b/doc/RUNMODEM.md index 1515172b24..b747775860 100644 --- a/doc/RUNMODEM.md +++ b/doc/RUNMODEM.md @@ -544,7 +544,7 @@ sudo ./nr-uesoftmodem --do-ra #### Run OAI with SDAP & Custom DRBs -To run OAI gNB with SDAP, simply include `--gNBs.[0].enable_sdap 1` to the binary's arguments. +SDAP is enabled by default. To disable SDAP, include `--gNBs.[0].enable_sdap 0` to the binary's arguments. The DRB creation is dependent on the 5QI. If the 5QI corresponds to a GBR Flow it assigns a dedicated data radio bearer. diff --git a/openair2/COMMON/e1ap_messages_types.h b/openair2/COMMON/e1ap_messages_types.h index 711c126dab..48cfef98f3 100644 --- a/openair2/COMMON/e1ap_messages_types.h +++ b/openair2/COMMON/e1ap_messages_types.h @@ -268,8 +268,8 @@ typedef struct up_params_s { /* IE SDAP Configuration (clause 9.3.1.39 of 3GPP TS 38.463) */ typedef struct bearer_context_sdap_config_s { long defaultDRB; - long sDAP_Header_UL; - long sDAP_Header_DL; + bool sDAP_Header_UL; + bool sDAP_Header_DL; } bearer_context_sdap_config_t; /* IE PDCP Configuration (clause 9.3.1.38 of 3GPP TS 38.463) */ @@ -404,11 +404,17 @@ typedef struct DRB_nGRAN_to_modify_s { // 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; + // Flow Mapping Information (O) (clause 9.3.1.25, 9.3.1.26) + int numQosFlowsMod; qos_flow_to_setup_t qosFlows[E1AP_MAX_NUM_QOS_FLOWS]; } DRB_nGRAN_to_mod_t; +/* DRB To Remove Item (NG-RAN) clause 9.3.1.11 */ +typedef struct { + // DRB ID (M) + long id; +} drb_to_remove_t; + typedef enum e1ap_indication_e { SECURITY_REQUIRED = 0, SECURITY_PREFERRED, @@ -472,6 +478,9 @@ typedef struct pdu_session_to_mod_s { long numDRB2Modify; // DRB To Modify Item (1..) DRB_nGRAN_to_mod_t DRBnGRanModList[E1AP_MAX_NUM_DRBS]; + // DRB To Remove List (0..maxnoofDRBs) + int n_drb_to_remove; + drb_to_remove_t drbs_to_remove[E1AP_MAX_NUM_DRBS]; } pdu_session_to_mod_t; /** PDU Session Resource To Remove List (3GPP TS 38.463 clause 9.3.3.12) */ diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index b7c6c281ec..111a131f39 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -226,18 +226,21 @@ typedef struct pdusession_setup_s { pdusession_associate_qosflow_t associated_qos_flows[MAX_QOS_FLOWS]; } pdusession_setup_t; +/* QoS Flow Add or Modify Response Item (3GPP TS 38.413 9.2.1.6) */ typedef struct qos_flow_tobe_modified_s { - uint8_t qfi; // 0~63 -} qos_flow_tobe_modified_t; + // QoS Flow Identifier + uint8_t qfi; +} qos_flow_addmod_response_item_t; +/* PDU Session Resource Modify Response Item (3GPP TS 38.413 9.2.1.6) */ typedef struct pdusession_modify_s { - /* Unique pdusession_id for the UE. */ + // PDU Session ID uint8_t pdusession_id; - + /* PDU Session Resource Modify Response Transfer */ + // QoS Flow Add or Modify Response List uint8_t nb_of_qos_flow; - // qos_flow_add_or_modify - qos_flow_tobe_modified_t qos[MAX_QOS_FLOWS]; + qos_flow_addmod_response_item_t qos[MAX_QOS_FLOWS]; } pdusession_modify_t; /* Cause (9.3.1.2 of 3GPP TS 38.413) */ @@ -889,17 +892,17 @@ typedef struct ngap_pdusession_modify_req_s { pdusession_resource_item_t pdusession[NGAP_MAX_PDU_SESSION]; } ngap_pdusession_modify_req_t; +/* 9.2.1.6 of 3GPP TS 38.413 */ typedef struct ngap_pdusession_modify_resp_s { + // RAN UE NGAP ID uint32_t gNB_ue_ngap_id; - - /* Number of pdusession modify-ed in the list */ + // AMF UE NGAP ID + uint64_t amf_ue_ngap_id; + // PDU Session Resource Modify Response List (0..256) uint16_t nb_of_pdusessions; - /* list of pdusession modify-ed by RRC layers */ pdusession_modify_t pdusessions[NGAP_MAX_PDU_SESSION]; - - /* Number of pdusession failed to be modify in list */ + // PDU Session Resource Failed to Modify List (0..256) uint16_t nb_of_pdusessions_failed; - /* list of pdusessions that failed to be modify */ pdusession_failed_t pdusessions_failed[NGAP_MAX_PDU_SESSION]; } ngap_pdusession_modify_resp_t; diff --git a/openair2/COMMON/rrc_messages_types.h b/openair2/COMMON/rrc_messages_types.h index cfc6914f4b..6b69959168 100644 --- a/openair2/COMMON/rrc_messages_types.h +++ b/openair2/COMMON/rrc_messages_types.h @@ -422,7 +422,6 @@ typedef struct NRRrcConfigurationReq_s { bool um_on_default_drb; bool enable_sdap; - int drbs; } gNB_RrcConfigurationReq; typedef struct NRDuDlReq_s { diff --git a/openair2/E1AP/lib/e1ap_bearer_context_management.c b/openair2/E1AP/lib/e1ap_bearer_context_management.c index f2593f103c..15c472b10d 100644 --- a/openair2/E1AP/lib/e1ap_bearer_context_management.c +++ b/openair2/E1AP/lib/e1ap_bearer_context_management.c @@ -192,8 +192,8 @@ static E1AP_SDAP_Configuration_t e1_encode_sdap_config(const bearer_context_sdap { 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; + out.sDAP_Header_UL = in->sDAP_Header_UL ? E1AP_SDAP_Header_UL_present : E1AP_SDAP_Header_UL_absent; + out.sDAP_Header_DL = in->sDAP_Header_DL ? E1AP_SDAP_Header_DL_present : E1AP_SDAP_Header_DL_absent; return out; } @@ -201,8 +201,8 @@ static E1AP_SDAP_Configuration_t e1_encode_sdap_config(const bearer_context_sdap 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; + out->sDAP_Header_UL = in->sDAP_Header_UL == E1AP_SDAP_Header_UL_present; + out->sDAP_Header_DL = in->sDAP_Header_DL == E1AP_SDAP_Header_DL_present; return true; } @@ -212,8 +212,8 @@ static bool e1_decode_sdap_config(bearer_context_sdap_config_t *out, const E1AP_ static bool eq_sdap_config(const bearer_context_sdap_config_t *a, const bearer_context_sdap_config_t *b) { _E1_EQ_CHECK_LONG(a->defaultDRB, b->defaultDRB); - _E1_EQ_CHECK_LONG(a->sDAP_Header_UL, b->sDAP_Header_UL); - _E1_EQ_CHECK_LONG(a->sDAP_Header_DL, b->sDAP_Header_DL); + _E1_EQ_CHECK_INT(a->sDAP_Header_UL, b->sDAP_Header_UL); + _E1_EQ_CHECK_INT(a->sDAP_Header_DL, b->sDAP_Header_DL); return true; } @@ -1459,12 +1459,20 @@ static E1AP_PDU_Session_Resource_To_Modify_Item_t e1_encode_pdu_session_to_mod_i } } // QoS Flows to setup (O) - for (const qos_flow_to_setup_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlow2Setup; k++) { + for (const qos_flow_to_setup_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlowsMod; 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); } } + // DRB To Remove List (O) + if (in->n_drb_to_remove > 0) { + asn1cCalloc(out.dRB_To_Remove_List_NG_RAN, drb2Remove_List); + for (int r = 0; r < in->n_drb_to_remove; r++) { + asn1cSequenceAdd(drb2Remove_List->list, E1AP_DRB_To_Remove_Item_NG_RAN_t, drb2Rem); + drb2Rem->dRB_ID = in->drbs_to_remove[r].id; + } + } return out; } @@ -1740,12 +1748,21 @@ static bool e1_decode_pdu_session_to_mod_item(pdu_session_to_mod_t *out, const E // QoS Flows Information To Be Setup (O) if (drb2Mod->flow_Mapping_Information) { E1AP_QoS_Flow_QoS_Parameter_List_t *qos2SetupList = drb2Mod->flow_Mapping_Information; - drb->numQosFlow2Setup = qos2SetupList->list.count; + drb->numQosFlowsMod = 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])); } } } + // DRB To Remove List (O) + if (in->dRB_To_Remove_List_NG_RAN) { + E1AP_DRB_To_Remove_List_NG_RAN_t *rm = in->dRB_To_Remove_List_NG_RAN; + out->n_drb_to_remove = rm->list.count; + for (int r = 0; r < rm->list.count; r++) { + E1AP_DRB_To_Remove_Item_NG_RAN_t *item = rm->list.array[r]; + out->drbs_to_remove[r].id = item->dRB_ID; + } + } return true; } @@ -1931,6 +1948,9 @@ static pdu_session_to_mod_t cp_pdu_session_to_mod_item(const pdu_session_to_mod_ // DRB to modify list for (int j = 0; j < msg->numDRB2Modify; j++) cp.DRBnGRanModList[j] = cp_drb_to_mod_item(&msg->DRBnGRanModList[j]); + // DRB to remove list + for (int r = 0; r < msg->n_drb_to_remove; r++) + cp.drbs_to_remove[r] = msg->drbs_to_remove[r]; _E1_CP_OPTIONAL_IE(&cp, msg, securityIndication); _E1_CP_OPTIONAL_IE(&cp, msg, UP_TL_information); return cp; @@ -1972,8 +1992,8 @@ static bool eq_security_ind(security_indication_t *a, security_indication_t *b) static bool eq_drb_to_mod(const DRB_nGRAN_to_mod_t *a, const DRB_nGRAN_to_mod_t *b) { _E1_EQ_CHECK_LONG(a->id, b->id); - _E1_EQ_CHECK_INT(a->numQosFlow2Setup, b->numQosFlow2Setup); - for (int i = 0; i < a->numQosFlow2Setup; i++) { + _E1_EQ_CHECK_INT(a->numQosFlowsMod, b->numQosFlowsMod); + for (int i = 0; i < a->numQosFlowsMod; i++) { if (!eq_qos_flow(&a->qosFlows[i], &b->qosFlows[i])) return false; } @@ -2021,6 +2041,10 @@ static bool eq_pdu_session_to_mod_item(const pdu_session_to_mod_t *a, const pdu_ return false; } _E1_EQ_CHECK_OPTIONAL_PTR(a, b, securityIndication); + _E1_EQ_CHECK_INT(a->n_drb_to_remove, b->n_drb_to_remove); + for (int r = 0; r < a->n_drb_to_remove; r++) { + _E1_EQ_CHECK_LONG(a->drbs_to_remove[r].id, b->drbs_to_remove[r].id); + } if (a->securityIndication && b->securityIndication) { if (!eq_security_ind(a->securityIndication, b->securityIndication)) return false; diff --git a/openair2/E1AP/lib/e1ap_lib_includes.h b/openair2/E1AP/lib/e1ap_lib_includes.h index adafbe0ae5..f58875a306 100644 --- a/openair2/E1AP/lib/e1ap_lib_includes.h +++ b/openair2/E1AP/lib/e1ap_lib_includes.h @@ -76,6 +76,8 @@ #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_DRB-To-Remove-List-NG-RAN.h" +#include "E1AP_DRB-To-Remove-Item-NG-RAN.h" #include "E1AP_PDU-Session-Resource-To-Setup-Mod-Item.h" #include "E1AP_DRB-To-Setup-Mod-Item-NG-RAN.h" #include "E1AP_PDU-Session-Resource-To-Remove-Item.h" diff --git a/openair2/E1AP/tests/e1ap_lib_test.c b/openair2/E1AP/tests/e1ap_lib_test.c index 50116514fb..6a94e0df23 100644 --- a/openair2/E1AP/tests/e1ap_lib_test.c +++ b/openair2/E1AP/tests/e1ap_lib_test.c @@ -505,7 +505,7 @@ static void test_bearer_context_modification_request(void) .pdcp_config = malloc_or_fail(sizeof(*drb_to_mod.pdcp_config)), .pdcp_sn_status_requested = true, .pdcp_status = malloc_or_fail(sizeof(*drb_to_mod.pdcp_status)), - .numQosFlow2Setup = 1, + .numQosFlowsMod = 1, .qosFlows[0] = dummy_qos_flows, }; *drb_to_mod.pdcp_config = dummy_pdcp_config; @@ -515,6 +515,8 @@ static void test_bearer_context_modification_request(void) .sessionId = 1, .numDRB2Modify = 1, .DRBnGRanModList[0] = drb_to_mod, + .n_drb_to_remove = 1, + .drbs_to_remove[0].id = 2, }; DRB_nGRAN_to_setup_t drb_to_setup = { diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 0b3845f5b9..01c44650ae 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -717,7 +717,6 @@ void RCconfig_verify(configmodule_interface_t *cfg, ngran_node_t node_type) } else if (NODE_IS_DU(node_type)) { // verify that there is no bearer config verify_gnb_param_notset(gnbp, GNB_ENABLE_SDAP_IDX, GNB_CONFIG_STRING_ENABLE_SDAP); - verify_gnb_param_notset(gnbp, GNB_DRBS, GNB_CONFIG_STRING_DRBS); verify_section_notset(cfg, GNB_CONFIG_STRING_GNB_LIST ".[0]", GNB_CONFIG_STRING_AMF_IP_ADDRESS); verify_section_notset(cfg, NULL, CONFIG_STRING_SECURITY); @@ -2092,9 +2091,7 @@ gNB_RRC_INST *RCconfig_NRRRC() nrrrc_config.num_plmn = set_plmn_config(nrrrc_config.plmn, k); nrrrc_config.enable_sdap = *GNBParamList.paramarray[i][GNB_ENABLE_SDAP_IDX].iptr; LOG_I(GNB_APP, "SDAP layer is %s\n", nrrrc_config.enable_sdap ? "enabled" : "disabled"); - nrrrc_config.drbs = *GNBParamList.paramarray[i][GNB_DRBS].iptr; nrrrc_config.um_on_default_drb = *(GNBParamList.paramarray[i][GNB_UMONDEFAULTDRB_IDX].uptr); - LOG_I(GNB_APP, "Data Radio Bearer count %d\n", nrrrc_config.drbs); }// }//End for (k=0; k cnAssociation->choice.sdap_Config, sdap_config); sdap_config->pdu_Session = pdu->sessionId; /* SDAP */ - sdap_config->sdap_HeaderDL = drb->sdap_config.sDAP_Header_DL; - sdap_config->sdap_HeaderUL = drb->sdap_config.sDAP_Header_UL; + /* Convert from internal representation to ASN.1 enum: + * Internal: false=absent, true=present (or uninitialized=false means absent) + * ASN.1: 0=present, 1=absent */ + sdap_config->sdap_HeaderDL = drb->sdap_config.sDAP_Header_DL ? NR_SDAP_Config__sdap_HeaderDL_present : NR_SDAP_Config__sdap_HeaderDL_absent; + sdap_config->sdap_HeaderUL = drb->sdap_config.sDAP_Header_UL ? NR_SDAP_Config__sdap_HeaderUL_present : NR_SDAP_Config__sdap_HeaderUL_absent; sdap_config->defaultDRB = drb->sdap_config.defaultDRB; asn1cCalloc(sdap_config->mappedQoS_FlowsToAdd, FlowsToAdd); for (int j = 0; j < drb->numQosFlow2Setup; j++) { diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 46e1ef3384..08e0d27888 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -166,6 +166,11 @@ typedef struct { pdusession_level_qos_parameter_t qos; } nr_rrc_qos_t; +typedef struct { + uint64_t dl_br; + uint64_t ul_br; +} nr_rrc_ambr_t; + /* forward declaration */ typedef struct nr_handover_context_s nr_handover_context_t; @@ -244,6 +249,9 @@ typedef struct gNB_RRC_UE_s { int n_initial_pdu; pdusession_t *initial_pdus; + // Aggregate Maximum Bit Rate + nr_rrc_ambr_t ambr; + /* Nas Pdu */ byte_array_t nas_pdu; diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 1db6a4d236..28fffd2cda 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -39,6 +39,7 @@ #include "NR_CG-ConfigInfo.h" #include "NR_RRCReconfiguration.h" #include "RRC/NR/MESSAGES/asn1_msg.h" +#include "f1ap_messages_types.h" #define SRB1 1 #define SRB2 2 diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 6b914c27e9..39c1d31490 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1963,7 +1963,7 @@ static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, * up security and request capabilities, so trigger PDU sessions now. The * UE NAS message will be forwarded in the corresponding reconfiguration, * the Initial context setup response after reconfiguration complete. */ - if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) { + if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, UE->ambr.dl_br)) { LOG_W(NR_RRC, "Failed to setup bearers for UE %d: send Initial Context Setup Response\n", UE->rrc_ue_id); rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE); rrc_forward_ue_nas_message(rrc, UE); @@ -2213,7 +2213,7 @@ static int rrc_gNB_decode_dcch(gNB_RRC_INST *rrc, const f1ap_ul_rrc_message_t *m * to set up security, so trigger PDU sessions now. The UE NAS * message will be forwarded in the corresponding reconfiguration, * the Initial context setup response after reconfiguration complete. */ - if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) { + if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, UE->ambr.dl_br)) { LOG_W(NR_RRC, "Failed to setup bearers for UE %d: send Initial Context Setup Response\n", UE->rrc_ue_id); rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE); rrc_forward_ue_nas_message(rrc, UE); @@ -2852,6 +2852,104 @@ f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qo return qos_char; } +static f1ap_drb_info_nr_t fill_f1_drb_info_nr(pdusession_t *pdu, const uint8_t *qfis, const int num_qfis) +{ + f1ap_drb_info_nr_t drb_info = {0}; + drb_info.nssai = pdu->nssai; + drb_info.flows_len = num_qfis; + drb_info.flows = calloc_or_fail(num_qfis, sizeof(*drb_info.flows)); + for (int i = 0; i < num_qfis; i++) { + int qfi = qfis[i]; + /* find the stored QoS flow parameters for this QFI */ + nr_rrc_qos_t *qos_param = find_qos(&pdu->qos, qfi); + DevAssert(qos_param); + /* QoS flows Flows Mapped to DRB Item */ + f1ap_drb_flows_mapped_t *flow = &drb_info.flows[i]; + flow->qfi = qfi; + flow->param = get_qos_char_from_qos_flow_param(&qos_param->qos); + } + /* DRB QoS IE: we just reuse the ones from the first flow */ + drb_info.drb_qos = drb_info.flows[0].param; + return drb_info; +} + +/** @brief Fill F1 DRB to Be Setup List (UE Context Setup Request) + * @param rrc RRC instance + * @param rrc_drb RRC DRB structure + * @param pdu PDU session parameters + * @param qfis List of QFIs + * @param num_qfis Number of QFIs in list + * @return Filled F1 DRB to setup structure */ +static f1ap_drb_to_setup_t fill_f1_drb_to_be_setup(const gNB_RRC_INST *rrc, + const drb_t *rrc_drb, + rrc_pdu_session_param_t *pdu, + const uint8_t *qfis, + int num_qfis) +{ + DevAssert(rrc); + DevAssert(rrc_drb); + DevAssert(pdu); + DevAssert(qfis); + DevAssert(num_qfis > 0); + DevAssert(num_qfis <= MAX_QOS_FLOWS); + + f1ap_drb_to_setup_t drb = {0}; + + drb.id = rrc_drb->drb_id; + drb.qos_choice = F1AP_QOS_CHOICE_NR; + /* DRB Info (including QoS flows) */ + drb.nr = fill_f1_drb_info_nr(&pdu->param, qfis, num_qfis); + + /* tunnel info */ + if (rrc_drb->cuup_tunnel_config.addr.length != sizeof(in_addr_t)) { + LOG_W(RRC, + "IPv4 address expected (length == %zu), got length %d\n", + sizeof(in_addr_t), + rrc_drb->cuup_tunnel_config.addr.length); + } + memcpy(&drb.up_ul_tnl[0].tl_address, rrc_drb->cuup_tunnel_config.addr.buffer, sizeof(in_addr_t)); + drb.up_ul_tnl[0].teid = rrc_drb->cuup_tunnel_config.teid; + drb.up_ul_tnl_len = 1; + + /* RLC mode */ + drb.rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM; + + /* PDCP SN length */ + const nr_pdcp_configuration_t *pdcp = &rrc_drb->pdcp_config; + DevAssert(pdcp->drb.sn_size == 18 || pdcp->drb.sn_size == 12); + drb.dl_pdcp_sn_len = malloc_or_fail(sizeof(*drb.dl_pdcp_sn_len)); + *drb.dl_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; + drb.ul_pdcp_sn_len = malloc_or_fail(sizeof(*drb.ul_pdcp_sn_len)); + *drb.ul_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; + + return drb; +} + +/** @brief Helper function to process DRBs from a PDU session's DRB setup list */ +static int fill_f1_drbs_from_e1(const gNB_RRC_INST *rrc, + gNB_RRC_UE_t *UE, + rrc_pdu_session_param_t *pdu, + const DRB_nGRAN_setup_t *drb_list, + const int drb_list_len, + f1ap_drb_to_setup_t drbs[32]) +{ + int nb_drb = 0; + for (int i = 0; i < drb_list_len; i++) { + const DRB_nGRAN_setup_t *drb_config = &drb_list[i]; + drb_t *rrc_drb = get_drb(&UE->drbs, drb_config->id); + DevAssert(rrc_drb); + int nb_qos_flows = drb_config->numQosFlowSetup; + DevAssert(nb_qos_flows <= MAX_QOS_FLOWS); + AssertFatal(nb_qos_flows > 0, "must map at least one flow to a DRB\n"); + uint8_t qfis[MAX_QOS_FLOWS] = {0}; + for (int j = 0; j < nb_qos_flows; j++) + qfis[j] = drb_config->qosFlows[j].qfi; + drbs[nb_drb++] = fill_f1_drb_to_be_setup(rrc, rrc_drb, pdu, qfis, nb_qos_flows); + } + DevAssert(nb_drb < MAX_DRBS_PER_UE); + return nb_drb; +} + /* \brief fills a list of DRBs to be setup from a number of PDU sessions in E1 * bearer setup response */ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc, @@ -2862,55 +2960,12 @@ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc, { int nb_drb = 0; for (int p = 0; p < numPduSession; ++p) { - rrc_pdu_session_param_t *pdu = find_pduSession(&UE->pduSessions, pduSession[p].id); + const pdu_session_setup_t *session = &pduSession[p]; + rrc_pdu_session_param_t *pdu = find_pduSession(&UE->pduSessions, session->id); DevAssert(pdu); - for (int i = 0; i < pduSession[p].numDRBSetup; i++) { - const DRB_nGRAN_setup_t *drb_config = &pduSession[p].DRBnGRanList[i]; - drb_t *rrc_drb = get_drb(&UE->drbs, pduSession[p].DRBnGRanList[i].id); - nr_pdcp_configuration_t pdcp = rrc_drb->pdcp_config; - DevAssert(rrc_drb); - - DevAssert(nb_drb < MAX_DRBS_PER_UE); - f1ap_drb_to_setup_t *drb = &drbs[nb_drb]; - drb->id = rrc_drb->drb_id; - - drb->qos_choice = F1AP_QOS_CHOICE_NR; - - /* pass QoS info to MAC */ - int nb_qos_flows = drb_config->numQosFlowSetup; - AssertFatal(nb_qos_flows > 0, "must map at least one flow to a DRB\n"); - drb->nr.flows_len = nb_qos_flows; - drb->nr.flows = calloc_or_fail(nb_qos_flows, sizeof(*drb->nr.flows)); - for (int j = 0; j < nb_qos_flows; j++) { - int qfi = drb_config->qosFlows[j].qfi; - /* find the QoS characteristics stored at RRC based on QFI returned - * from E1. We expect this to correspond to a QFI we passed to E1 - * previously, so we expect it to exist. */ - nr_rrc_qos_t *qos_param = find_qos(&pdu->param.qos, qfi); - DevAssert(qos_param); - f1ap_drb_flows_mapped_t *flow = &drb->nr.flows[j]; - flow->qfi = qfi; - flow->param = get_qos_char_from_qos_flow_param(&qos_param->qos); - } - /* the DRB QoS parameters: we just reuse the ones from the first flow */ - drb->nr.drb_qos = drb->nr.flows[0].param; - - /* pass NSSAI info to MAC */ - drb->nr.nssai = pdu->param.nssai; - - drb->up_ul_tnl[0].tl_address = drb_config->UpParamList[0].tl_info.tlAddress; - drb->up_ul_tnl[0].teid = drb_config->UpParamList[0].tl_info.teId; - drb->up_ul_tnl_len = 1; - drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM; - DevAssert(pdcp.drb.sn_size == 18 || pdcp.drb.sn_size == 12); - drb->dl_pdcp_sn_len = malloc_or_fail(sizeof(*drb->dl_pdcp_sn_len)); - *drb->dl_pdcp_sn_len = pdcp.drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; - drb->ul_pdcp_sn_len = malloc_or_fail(sizeof(*drb->ul_pdcp_sn_len)); - *drb->ul_pdcp_sn_len = pdcp.drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; - - nb_drb++; - } + nb_drb += fill_f1_drbs_from_e1(rrc, UE, pdu, session->DRBnGRanList, session->numDRBSetup, drbs); } + DevAssert(nb_drb < MAX_DRBS_PER_UE); return nb_drb; } @@ -3261,7 +3316,7 @@ void *rrc_gnb_task(void *args_p) { break; case NGAP_PDUSESSION_MODIFY_REQ: - rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(msg_p, instance); + rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(&NGAP_PDUSESSION_MODIFY_REQ(msg_p), instance); break; case NGAP_PDUSESSION_RELEASE_COMMAND: @@ -3554,39 +3609,23 @@ static int rrc_fill_f1_drb_to_setup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t FOR_EACH_SEQ_ARR(drb_t *, rrc_drb, &ue->drbs) { DevAssert(nb_drb < MAX_DRBS_PER_UE); - f1ap_drb_to_setup_t *drb = &drbs[nb_drb]; - nr_pdcp_configuration_t *pdcp = &rrc_drb->pdcp_config; - nb_drb++; /* fetch an existing PDU session for this DRB */ rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId((gNB_RRC_UE_t *)ue, rrc_drb->drb_id); AssertFatal(pdu != NULL, "no PDU session for DRB ID %d\n", rrc_drb->drb_id); - drb->id = rrc_drb->drb_id; + /* Collect all QFIs mapped to this DRB */ + uint8_t qfis[MAX_QOS_FLOWS] = {0}; + int num_qfis = 0; + FOR_EACH_SEQ_ARR (nr_rrc_qos_t *, qos, &pdu->param.qos) { + if (qos->drb_id == rrc_drb->drb_id) { + DevAssert(num_qfis < MAX_QOS_FLOWS); + qfis[num_qfis++] = qos->qos.qfi; + } + } + AssertFatal(num_qfis > 0, "no QoS flows mapped to DRB ID %d\n", rrc_drb->drb_id); - drb->qos_choice = F1AP_QOS_CHOICE_NR; - drb->nr.nssai = pdu->param.nssai; - drb->nr.flows_len = 1; - drb->nr.flows = calloc_or_fail(1, sizeof(*drb->nr.flows)); - - /* QoS flow associated with this DRB: use first QoS flow */ - AssertFatal(seq_arr_size(&pdu->param.qos) == 1, "only 1 Qos flow supported\n"); - nr_rrc_qos_t *qos_param = (nr_rrc_qos_t *)seq_arr_at(&pdu->param.qos, 0); - DevAssert(qos_param->qos.qfi > 0); - drb->nr.flows[0].qfi = qos_param->qos.qfi; - drb->nr.flows[0].param = get_qos_char_from_qos_flow_param(&qos_param->qos); - /* the DRB QoS parameters: reuse the ones from the first flow */ - drb->nr.drb_qos = drb->nr.flows[0].param; - - memcpy(&drb->up_ul_tnl[0].tl_address, &rrc_drb->cuup_tunnel_config.addr.buffer, sizeof(uint8_t) * 4); - drb->up_ul_tnl[0].teid = rrc_drb->cuup_tunnel_config.teid; - drb->up_ul_tnl_len = 1; - - drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM; - DevAssert(pdcp->drb.sn_size == 18 || pdcp->drb.sn_size == 12); - drb->dl_pdcp_sn_len = malloc_or_fail(sizeof(*drb->dl_pdcp_sn_len)); - *drb->dl_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; - drb->ul_pdcp_sn_len = malloc_or_fail(sizeof(*drb->ul_pdcp_sn_len)); - *drb->ul_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; + /* Use the common function to fill DRB setup */ + drbs[nb_drb++] = fill_f1_drb_to_be_setup(rrc, rrc_drb, pdu, qfis, num_qfis); } return nb_drb; } diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index bf8a797a49..f91e346f02 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -289,6 +289,31 @@ void rrc_gNB_send_NGAP_NAS_FIRST_REQ(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, NR_RRC itti_send_msg_to_task(TASK_NGAP, rrc->module_id, message_p); } +/// @brief set QoS Flows to Setup in E1 DRB To Setup List +static qos_flow_to_setup_t fill_e1_qos_flow_to_setup(const pdusession_level_qos_parameter_t *qos) +{ + qos_flow_to_setup_t qos_flow = { .qfi = qos->qfi }; + // ARP + ngran_allocation_retention_priority_t *arp_out = &qos_flow.qos_params.alloc_reten_priority; + const qos_arp_t *arp_in = &qos->arp; + arp_out->priority_level = arp_in->priority_level; + arp_out->preemption_capability = arp_in->pre_emp_capability; + arp_out->preemption_vulnerability = arp_in->pre_emp_vulnerability; + // QoS Characteristics + qos_characteristics_t *qos_characteristics = &qos_flow.qos_params.qos_characteristics; + if (qos->fiveQI_type == NON_DYNAMIC) { + qos_characteristics->non_dynamic.fiveqi = qos->fiveQI; + qos_characteristics->non_dynamic.qos_priority_level = qos->qos_priority; + } else { + qos_characteristics->dynamic.fiveqi = qos->fiveQI; + qos_characteristics->dynamic.qos_priority_level = qos->qos_priority; + // NOTE: missing packet error rate and delay budget + } + qos_characteristics->qos_type = qos->fiveQI_type; + + return qos_flow; +} + /** @brief Returns an instance of E1AP DRB To Setup List */ static DRB_nGRAN_to_setup_t fill_e1_drb_to_setup(const drb_t *rrc_drb, const pdusession_t *session, @@ -299,8 +324,8 @@ static DRB_nGRAN_to_setup_t fill_e1_drb_to_setup(const drb_t *rrc_drb, drb_ngran.id = rrc_drb->drb_id; drb_ngran.sdap_config.defaultDRB = true; - drb_ngran.sdap_config.sDAP_Header_UL = session->sdap_config.header_ul_absent ? 1 : 0; - drb_ngran.sdap_config.sDAP_Header_DL = session->sdap_config.header_dl_absent ? 1 : 0; + drb_ngran.sdap_config.sDAP_Header_UL = session->sdap_config.header_ul_absent ? false : true; + drb_ngran.sdap_config.sDAP_Header_DL = session->sdap_config.header_dl_absent ? false : true; drb_ngran.pdcp_config = set_bearer_context_pdcp_config(rrc_drb->pdcp_config, um_on_default_drb, redcap_cap); @@ -310,26 +335,13 @@ static DRB_nGRAN_to_setup_t fill_e1_drb_to_setup(const drb_t *rrc_drb, } FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) { - pdusession_level_qos_parameter_t *qos_session = &qos->qos; + if (qos->drb_id != drb_ngran.id) + continue; + const pdusession_level_qos_parameter_t *qos_session = &qos->qos; DevAssert(drb_ngran.numQosFlow2Setup < MAX_QOS_FLOWS); - qos_flow_to_setup_t *qos_flow = &drb_ngran.qosFlows[drb_ngran.numQosFlow2Setup++]; - qos_characteristics_t *qos_char = &qos_flow->qos_params.qos_characteristics; - qos_flow->qfi = qos_session->qfi; - qos_char->qos_type = qos_session->fiveQI_type; - if (qos_char->qos_type == DYNAMIC) { - qos_char->dynamic.fiveqi = qos_session->fiveQI; - qos_char->dynamic.qos_priority_level = qos_session->qos_priority; - } else { - qos_char->non_dynamic.fiveqi = qos_session->fiveQI; - qos_char->non_dynamic.qos_priority_level = qos_session->qos_priority; - } - - ngran_allocation_retention_priority_t *arp_out = &qos_flow->qos_params.alloc_reten_priority; - qos_arp_t *arp_in = &qos_session->arp; - arp_out->priority_level = arp_in->priority_level; - arp_out->preemption_capability = arp_in->pre_emp_capability; - arp_out->preemption_vulnerability = arp_in->pre_emp_vulnerability; + drb_ngran.qosFlows[drb_ngran.numQosFlow2Setup++] = fill_e1_qos_flow_to_setup(qos_session); } + DevAssert(drb_ngran.numQosFlow2Setup > 0); return drb_ngran; } @@ -355,6 +367,10 @@ static nr_sdap_configuration_t get_sdap_config(const bool enable_sdap) */ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession_t *sessions, uint64_t ueAggMaxBitRateDownlink) { + if (ueAggMaxBitRateDownlink == UINT64_MAX) { + LOG_E(NR_RRC, "UE %d: UE aggregate maximum bitrate must be known by the NG-RAN node\n", UE->rrc_ue_id); + return false; + } AssertFatal(UE->as_security_active, "logic bug: security should be active when activating DRBs\n"); e1ap_bearer_setup_req_t bearer_req = {0}; @@ -485,12 +501,15 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t UE->nas_pdu = req->nas_pdu; if (req->nb_of_pdusessions > 0) { + AssertFatal(req->has_ue_ambr, "UE aggregate maximum bitrate is required when there are PDU sessions to setup"); /* if there are PDU sessions to setup, store them to be created once * security (and UE capabilities) are received */ UE->n_initial_pdu = req->nb_of_pdusessions; UE->initial_pdus = calloc_or_fail(UE->n_initial_pdu, sizeof(*UE->initial_pdus)); for (int i = 0; i < UE->n_initial_pdu; ++i) cp_pdusession_resource_item_to_pdusession(&UE->initial_pdus[i], &req->pdusession[i]); + UE->ambr.dl_br = req->ue_ambr.br_dl; + UE->ambr.ul_br = req->ue_ambr.br_ul; } /* security */ @@ -513,7 +532,7 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t // do not remove the above allocation which is reused here: this is used // in handle_rrcReconfigurationComplete() to know that we need to send a // Initial context setup response message - if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) { + if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, UE->ambr.dl_br)) { LOG_W(NR_RRC, "UE %d: reject PDU Session Setup in Initial Context Setup Response\n", UE->rrc_ue_id); ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_RESOURCES_NOT_AVAILABLE_FOR_THE_SLICE}; send_ngap_initial_context_setup_resp_fail(rrc->module_id, req, cause); @@ -866,9 +885,13 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins for (int i = 0; i < msg->nb_pdusessions_tosetup; ++i) cp_pdusession_resource_item_to_pdusession(&to_setup[i], &msg->pdusession[i]); - uint64_t dl_ambr = msg->has_ue_ambr ? msg->ueAggMaxBitRate.br_dl : 0; + /** Set AMBR if provided (optional) */ + if (msg->has_ue_ambr) { + UE->ambr.dl_br = msg->ueAggMaxBitRate.br_dl; + UE->ambr.ul_br = msg->ueAggMaxBitRate.br_ul; + } - if (!trigger_bearer_setup(rrc, UE, msg->nb_pdusessions_tosetup, to_setup, dl_ambr)) { + if (!trigger_bearer_setup(rrc, UE, msg->nb_pdusessions_tosetup, to_setup, UE->ambr.dl_br)) { // Reject PDU Session Resource setup if there's no CU-UP associated LOG_W(NR_RRC, "UE %d: reject PDU Session Setup in PDU Session Resource Setup Response\n", UE->rrc_ue_id); ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_RESOURCES_NOT_AVAILABLE_FOR_THE_SLICE}; @@ -911,33 +934,64 @@ static void nr_rrc_update_pdusession(pdusession_t *dst, const pdusession_resourc } //------------------------------------------------------------------------------ -int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t instance) -//------------------------------------------------------------------------------ +/** @brief Handle abnormal conditions in PDU Session Modify procedure. + * Send a response with failed of PDU sessions only, for all PDU Sessions in the request */ +static int rrc_gNB_ngap_pdusession_mod_failure(int module_id, const ngap_pdusession_modify_req_t *req, const ngap_cause_t cause) { - rrc_gNB_ue_context_t *ue_context_p = NULL; - - ngap_pdusession_modify_req_t *req = &NGAP_PDUSESSION_MODIFY_REQ(msg_p); + MessageDef *msg_p = itti_alloc_new_message (TASK_RRC_GNB, module_id, NGAP_PDUSESSION_MODIFY_RESP); + if (msg_p == NULL) { + LOG_E(NR_RRC, "itti_alloc_new_message failed, msg_p is NULL \n"); + return -1; + } + ngap_pdusession_modify_resp_t *resp = &NGAP_PDUSESSION_MODIFY_RESP(msg_p); + resp->amf_ue_ngap_id = req->amf_ue_ngap_id; + resp->gNB_ue_ngap_id = req->gNB_ue_ngap_id; + resp->nb_of_pdusessions_failed = req->nb_pdusessions_tomodify; + resp->pdusessions_failed->cause = cause; + for (int i = 0; i < req->nb_pdusessions_tomodify; i++) + resp->pdusessions_failed[i].pdusession_id = req->pdusession[i].pdusession_id; + itti_send_msg_to_task(TASK_NGAP, module_id, msg_p); + return 0; +} +int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(const ngap_pdusession_modify_req_t *req, instance_t instance) +{ gNB_RRC_INST *rrc = RC.nrrrc[instance]; - ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_ue_ngap_id); + + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_ue_ngap_id); if (ue_context_p == NULL) { LOG_W(NR_RRC, "[gNB %ld] In NGAP_PDUSESSION_MODIFY_REQ: unknown UE from NGAP ids (%u)\n", instance, req->gNB_ue_ngap_id); - // TO implement return setup failed + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_INCONSISTENT_REMOTE_UE_NGAP_ID}; + rrc_gNB_ngap_pdusession_mod_failure(rrc->module_id, req, cause); return (-1); } gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + if (UE->amf_ue_ngap_id != req->amf_ue_ngap_id) { + LOG_W(NR_RRC, + "Stored amf_ue_ngap_id %ld for UE %x does not match the requested one %ld\n", + UE->amf_ue_ngap_id, + UE->rrc_ue_id, + req->amf_ue_ngap_id); + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_INCONSISTENT_REMOTE_UE_NGAP_ID}; + rrc_gNB_ngap_pdusession_mod_failure(rrc->module_id, req, cause); + return -1; + } + + uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id); bool all_failed = true; + DevAssert(req->nb_pdusessions_tomodify <= NGAP_MAX_PDU_SESSION); for (int i = 0; i < req->nb_pdusessions_tomodify; i++) { const pdusession_resource_item_t *sessMod = &req->pdusession[i]; rrc_pdu_session_param_t *session = find_pduSession(&UE->pduSessions, sessMod->pdusession_id); if (!session) { + /* 8.2.3.4 3GPP TS 38.413: If the NG-RAN node receives unrecognized PDU Session ID IEs, + the NG-RAN node shall report the corresponding invalid PDU sessions as failed. So we add it to the list. */ LOG_W(NR_RRC, "Requested modification of non-existing PDU session, refusing modification\n"); - rrc_pdu_session_param_t to_add = {0}; - to_add.status = PDU_SESSION_STATUS_FAILED; - to_add.param.pdusession_id = sessMod->pdusession_id; - to_add.cause.type = NGAP_CAUSE_RADIO_NETWORK; - to_add.cause.value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID; - seq_arr_push_back(&UE->pduSessions, &to_add, sizeof(to_add)); + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID}; + pdusession_t session = {.pdusession_id = sessMod->pdusession_id}; + rrc_pdu_session_param_t *added = add_pduSession(&UE->pduSessions, &session); + added->status = PDU_SESSION_STATUS_FAILED; + added->cause = cause; } else { all_failed = false; session->status = PDU_SESSION_STATUS_NEW; @@ -949,50 +1003,34 @@ int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t ins if (!all_failed) { rrc_gNB_modify_dedicatedRRCReconfiguration(rrc, UE); } else { - LOG_I(NR_RRC, - "pdu session modify failed, fill NGAP_PDUSESSION_MODIFY_RESP with the pdu session information that failed to modify \n"); MessageDef *msg_fail_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_MODIFY_RESP); if (msg_fail_p == NULL) { LOG_E(NR_RRC, "itti_alloc_new_message failed, msg_fail_p is NULL \n"); - return (-1); + return -1; } - ngap_pdusession_modify_resp_t *msg = &NGAP_PDUSESSION_MODIFY_RESP(msg_fail_p); - msg->gNB_ue_ngap_id = req->gNB_ue_ngap_id; - msg->nb_of_pdusessions = 0; - - FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) { - if (session->status == PDU_SESSION_STATUS_FAILED) { - msg->pdusessions_failed[msg->nb_of_pdusessions_failed].pdusession_id = session->param.pdusession_id; - msg->pdusessions_failed[msg->nb_of_pdusessions_failed].cause.type = session->cause.type; - msg->pdusessions_failed[msg->nb_of_pdusessions_failed].cause.value = session->cause.value; - msg->nb_of_pdusessions_failed++; - } - } - itti_send_msg_to_task(TASK_NGAP, instance, msg_fail_p); + rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(rrc, UE, xid); } return (0); } +/** @brief Send PDU Session Resource Setup Response (9.2.1.6 3GPP TS 38.413) + * Direction: Direction: NG-RAN node → AMF */ int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid) { - MessageDef *msg_p = NULL; - uint16_t pdu_sessions_failed = 0; - uint16_t pdu_sessions_done = 0; - if (!seq_arr_size(&UE->pduSessions)) { LOG_W(NR_RRC, "UE %d: No PDU sessions in the list, don't send NG PDU Session Modify Response\n", UE->rrc_ue_id); return -1; } - msg_p = itti_alloc_new_message (TASK_RRC_GNB, rrc->module_id, NGAP_PDUSESSION_MODIFY_RESP); + MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, rrc->module_id, NGAP_PDUSESSION_MODIFY_RESP); if (msg_p == NULL) { LOG_E(NR_RRC, "itti_alloc_new_message failed, msg_p is NULL \n"); return (-1); } - ngap_pdusession_modify_resp_t *resp = &NGAP_PDUSESSION_MODIFY_RESP(msg_p); - LOG_I(NR_RRC, "send message NGAP_PDUSESSION_MODIFY_RESP \n"); + ngap_pdusession_modify_resp_t *resp = &NGAP_PDUSESSION_MODIFY_RESP(msg_p); resp->gNB_ue_ngap_id = UE->rrc_ue_id; + resp->amf_ue_ngap_id = UE->amf_ue_ngap_id; FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) { if (xid != session->xid) { @@ -1005,47 +1043,35 @@ int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE continue; } if (session->status == PDU_SESSION_STATUS_DONE) { - LOG_I(NR_RRC, "Successfully modified PDU Session %d \n", session->param.pdusession_id); + LOG_I(NR_RRC, "PDU Session Modify successful (pdusession_id=%d) \n", session->param.pdusession_id); // Update status session->status = PDU_SESSION_STATUS_ESTABLISHED; session->cause.type = NGAP_CAUSE_NOTHING; // Fill response - DevAssert(pdu_sessions_done < NGAP_MAX_PDU_SESSION); - pdusession_modify_t *p = &resp->pdusessions[pdu_sessions_done++]; + DevAssert(resp->nb_of_pdusessions <= NGAP_MAX_PDU_SESSION); + pdusession_modify_t *p = &resp->pdusessions[resp->nb_of_pdusessions++]; p->pdusession_id = session->param.pdusession_id; FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &session->param.qos) { DevAssert(p->nb_of_qos_flow < MAX_QOS_FLOWS); - qos_flow_tobe_modified_t *q = &p->qos[p->nb_of_qos_flow++]; + qos_flow_addmod_response_item_t *q = &p->qos[p->nb_of_qos_flow++]; q->qfi = qos_session->qos.qfi; } - LOG_I(NR_RRC, - "Modify Resp (msg index %d, status %d, xid %d): nb_of_pduSessions %ld, pdusession_id %d \n ", - pdu_sessions_done, - session->status, - xid, - seq_arr_size(&UE->pduSessions), - p->pdusession_id); + p->pdusession_id = session->param.pdusession_id; } else if ((session->status == PDU_SESSION_STATUS_NEW) || (session->status == PDU_SESSION_STATUS_ESTABLISHED)) { LOG_D(NR_RRC, "PDU SESSION is NEW or already ESTABLISHED\n"); } else if (session->status == PDU_SESSION_STATUS_FAILED) { - DevAssert(pdu_sessions_failed < NGAP_MAX_PDU_SESSION); - resp->pdusessions_failed[pdu_sessions_failed].pdusession_id = session->param.pdusession_id; - resp->pdusessions_failed[pdu_sessions_failed].cause.type = session->cause.type; - resp->pdusessions_failed[pdu_sessions_failed].cause.value = session->cause.value; - pdu_sessions_failed++; + DevAssert(resp->nb_of_pdusessions_failed <= NGAP_MAX_PDU_SESSION); + pdusession_failed_t *failed = &resp->pdusessions_failed[resp->nb_of_pdusessions_failed++]; + failed->pdusession_id = session->param.pdusession_id; + failed->cause = session->cause; + rm_pduSession(&UE->pduSessions, &UE->drbs, session->param.pdusession_id); } else LOG_W(NR_RRC, "Modify pdu session %d, unknown state %d \n ", session->param.pdusession_id, session->status); } - resp->nb_of_pdusessions = pdu_sessions_done; - resp->nb_of_pdusessions_failed = pdu_sessions_failed; - - if (pdu_sessions_done > 0 || pdu_sessions_failed > 0) { - LOG_D(NR_RRC, "NGAP_PDUSESSION_MODIFY_RESP: sending the message (total pdu session %ld)\n", seq_arr_size(&UE->pduSessions)); - itti_send_msg_to_task (TASK_NGAP, rrc->module_id, msg_p); - } else { - itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p); - } + // Send message to NGAP (always send: if no PDU sessions, only mandatory IEs) + LOG_D(NR_RRC, "Send NG PDU Session Modify Response (nb_of_pdusessions %d, xid %d)\n", resp->nb_of_pdusessions, xid); + itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p); return 0; } @@ -1184,7 +1210,11 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga cp_pdusession_transfer_to_pdusession(pdu, &ho_pdu->pdusessionTransfer); } - if (!trigger_bearer_setup(rrc, UE, msg->nb_of_pdusessions, to_setup, msg->ue_ambr.br_dl)) { + // Store UE aggregate maximum bitrate + UE->ambr.dl_br = msg->ue_ambr.br_dl; + UE->ambr.ul_br = msg->ue_ambr.br_ul; + + if (!trigger_bearer_setup(rrc, UE, msg->nb_of_pdusessions, to_setup, UE->ambr.dl_br)) { LOG_E(NR_RRC, "Failed to establish PDU session: handover failed\n"); ngap_handover_failure_t fail = { @@ -1196,7 +1226,6 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga rrc_remove_ue(rrc, ue_context_p); return -1; } - return 0; } diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h index c3f692ff5f..2530b3a803 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -60,7 +60,7 @@ void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t instance); -int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t instance); +int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(const ngap_pdusession_modify_req_t *req, instance_t instance); int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid); diff --git a/openair2/RRC/NR/rrc_gNB_UE_context.c b/openair2/RRC/NR/rrc_gNB_UE_context.c index 86838927ab..c05b8a33a8 100644 --- a/openair2/RRC/NR/rrc_gNB_UE_context.c +++ b/openair2/RRC/NR/rrc_gNB_UE_context.c @@ -218,6 +218,8 @@ rrc_gNB_ue_context_t *rrc_gNB_create_ue_context(sctp_assoc_t assoc_id, ue->rrc_ue_id); bool success = cu_add_f1_ue_data(ue->rrc_ue_id, &ue_data); DevAssert(success); + ue->ambr.dl_br = UINT64_MAX; + ue->ambr.ul_br = UINT64_MAX; // Initialise setup PDU Sessions list seq_arr_init(&ue->pduSessions, sizeof(rrc_pdu_session_param_t)); diff --git a/openair3/NGAP/ngap_gNB_nas_procedures.c b/openair3/NGAP/ngap_gNB_nas_procedures.c index 7bf7753668..8a2cfeead5 100644 --- a/openair3/NGAP/ngap_gNB_nas_procedures.c +++ b/openair3/NGAP/ngap_gNB_nas_procedures.c @@ -1084,7 +1084,7 @@ int ngap_gNB_pdusession_modify_resp(instance_t instance, ngap_pdusession_modify_ ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; ie->criticality = NGAP_Criticality_ignore; ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_AMF_UE_NGAP_ID; - asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, ue_context_p->amf_ue_ngap_id); + asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, pdusession_modify_resp_p->amf_ue_ngap_id); asn1cSeqAdd(&out->protocolIEs.list, ie); } /* mandatory */ @@ -1115,12 +1115,11 @@ int ngap_gNB_pdusession_modify_resp(instance_t instance, ngap_pdusession_modify_ qos->qosFlowIdentifier = pdusession_modify_resp_p->pdusessions[i].qos[qos_flow_index].qfi; } asn_encode_to_new_buffer_result_t res = {0}; - NGAP_PDUSessionResourceModifyResponseTransfer_t *transfer_p = NULL; - res = asn_encode_to_new_buffer(NULL, ATS_ALIGNED_CANONICAL_PER, &asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, transfer_p); + res = asn_encode_to_new_buffer(NULL, ATS_ALIGNED_CANONICAL_PER, &asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, &transfer); item->pDUSessionResourceModifyResponseTransfer.buf = res.buffer; item->pDUSessionResourceModifyResponseTransfer.size = res.result.encoded; - ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, transfer_p); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, &transfer); NGAP_DEBUG("pdusession_modify_resp: modified pdusession ID %ld\n", item->pDUSessionID); } @@ -1143,12 +1142,11 @@ int ngap_gNB_pdusession_modify_resp(instance_t instance, ngap_pdusession_modify_ encode_ngap_cause(&pdusessionTransfer.cause, &pdusession_modify_resp_p->pdusessions_failed[i].cause); asn_encode_to_new_buffer_result_t res = {0}; - NGAP_PDUSessionResourceModifyUnsuccessfulTransfer_t *pdusessionTransfer_p = NULL; res = asn_encode_to_new_buffer(NULL, ATS_ALIGNED_CANONICAL_PER, &asn_DEF_NGAP_PDUSessionResourceModifyUnsuccessfulTransfer, &pdusessionTransfer); item->pDUSessionResourceModifyUnsuccessfulTransfer.buf = res.buffer; item->pDUSessionResourceModifyUnsuccessfulTransfer.size = res.result.encoded; - ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyUnsuccessfulTransfer, pdusessionTransfer_p); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyUnsuccessfulTransfer, &pdusessionTransfer); NGAP_INFO("pdusession_modify_resp: failed pdusession ID %ld\n", item->pDUSessionID); } diff --git a/tests/nr-cuup/nr-cuup-load-test.c b/tests/nr-cuup/nr-cuup-load-test.c index 0774b26b1b..047d316afc 100644 --- a/tests/nr-cuup/nr-cuup-load-test.c +++ b/tests/nr-cuup/nr-cuup-load-test.c @@ -418,7 +418,9 @@ static void *sender_thread(void *v) { struct thr_data *d = v; - size_t oh = d->is_ul ? 3 : 0; + // Overhead: PDCP header (2 bytes) for UL packets + // SDAP headers are disabled, so only PDCP header is added + size_t oh = d->is_ul ? 2 : 0; size_t packet_len = d->data_len + oh; uint8_t buf[packet_len]; memset(buf, 0, packet_len);