Merge remote-tracking branch 'origin/rrc-qos-refactor' into integration_2026_w07 (!3907)

RRC QoS refactor: PDU Session Modify support, SDAP fixes, and code improvements

The goal of this MR is to prepare the RRC/NGAP layer for the multi-QoS
support. The MR:

- enables PDU Session Resource Modify support in RRC/NGAP/E1AP
- fixes SDAP header mapping
- enables SDAP by default
- refactors QoS flow handling in RRC/NGAP
- improves error handling, and code maintainability in RRC/NGAP

Changes:

- NGAP PDU Session Modify:
  * add NGAP PDU Session Resource Modify Request/Response handling with
    proper error reporting for unrecognized sessions and AMF UE NGAP ID
    validation
- E1AP Bearer Context Modification:
  * add DRB To Remove List IE to support DRB release during modify
  * rename numQosFlow2Setup to numQosFlowsMod in DRB To Modify List
- RRC QoS flow handling:
  * enable multi-QoS flow support per DRB
  * add utilities to map NGAP QoS parameters to E1AP
  * filter QoS flows by DRB ID
- SDAP configuration:
  * Fix SDAP header UL/DL mapping between internal representation and
    ASN.1 enum (invert mapping)
  * change SDAP header fields from long to bool for clarity
- F1 DRB setup refactoring:
  * Extract shared helpers for F1 DRB setup list filling
  * reduce duplication between E1 response and direct DRB setup paths
- AMBR management:
  * Store AMBR in UE context
  * validate AMBR is set before bearer setup
- Configuration:
  * remove unused drbs configuration parameter
  * enable SDAP by default (change default from false to true)
This commit is contained in:
Robert Schmidt
2026-02-11 15:02:01 +01:00
21 changed files with 342 additions and 231 deletions
@@ -18,7 +18,7 @@ gNBs =
////////// Physical parameters:
min_rxtxtime = 6;
enable_sdap = 1;
enable_sdap = 0;
servingCellConfigCommon = (
{
@@ -18,7 +18,7 @@ gNBs =
////////// Physical parameters:
min_rxtxtime = 6;
enable_sdap = 1;
enable_sdap = 0;
servingCellConfigCommon = (
{
+1 -3
View File
@@ -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
+1 -1
View File
@@ -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.
+13 -4
View File
@@ -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..<E1AP_MAX_NUM_DRBS>)
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) */
+15 -12
View File
@@ -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;
-1
View File
@@ -422,7 +422,6 @@ typedef struct NRRrcConfigurationReq_s {
bool um_on_default_drb;
bool enable_sdap;
int drbs;
} gNB_RrcConfigurationReq;
typedef struct NRDuDlReq_s {
@@ -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;
+2
View File
@@ -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"
+3 -1
View File
@@ -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 = {
-3
View File
@@ -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 <num_gnbs ; k++)
+15 -20
View File
@@ -110,7 +110,6 @@ typedef enum {
#define GNB_CONFIG_STRING_MAXMIMOLAYERS "maxMIMO_layers"
#define GNB_CONFIG_STRING_DISABLE_HARQ "disable_harq"
#define GNB_CONFIG_STRING_ENABLE_SDAP "enable_sdap"
#define GNB_CONFIG_STRING_DRBS "drbs"
#define GNB_CONFIG_STRING_USE_DELTA_MCS "use_deltaMCS"
#define GNB_CONFIG_HLP_USE_DELTA_MCS "Use deltaMCS-based power headroom reporting in PUSCH-Config"
#define GNB_CONFIG_HLP_FORCEUL256QAMOFF "suppress activation of UL 256 QAM despite UE support"
@@ -130,7 +129,6 @@ typedef enum {
#define GNB_CONFIG_HLP_FORCE256QAMOFF "suppress activation of 256 QAM despite UE support"
#define GNB_CONFIG_HLP_MAXMIMOLAYERS "limit on maxMIMO-layers for DL"
#define GNB_CONFIG_HLP_DISABLE_HARQ "disable feedback for all HARQ processes (REL17 feature)"
#define GNB_CONFIG_HLP_STRING_DRBS "Number of total DRBs to establish, including the mandatory for PDU SEssion (default=1)\n"
#define GNB_CONFIG_HLP_GNB_DU_ID "defines the gNB-DU ID (only applicable for DU)"
#define GNB_CONFIG_HLP_GNB_CU_UP_ID "defines the gNB-CU-UP ID (only applicable for CU-UP)"
#define GNB_CONFIG_HLP_NUM_DL_HARQ "Set Num DL harq processes. Valid values 2,4,6,8,10,12,16,32. Default 16"
@@ -172,8 +170,7 @@ typedef enum {
{GNB_CONFIG_STRING_ULPRBBLACKLIST, NULL, 0, .strptr=NULL,.defstrval="", TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_UMONDEFAULTDRB, NULL, PARAMFLAG_BOOL, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
{GNB_CONFIG_STRING_FORCE256QAMOFF, GNB_CONFIG_HLP_FORCE256QAMOFF, PARAMFLAG_BOOL, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
{GNB_CONFIG_STRING_ENABLE_SDAP, GNB_CONFIG_HLP_STRING_ENABLE_SDAP, PARAMFLAG_BOOL,.iptr=NULL, .defintval=0, TYPE_INT, 0}, \
{GNB_CONFIG_STRING_DRBS, GNB_CONFIG_HLP_STRING_DRBS, 0, .iptr=NULL, .defintval=1, TYPE_INT, 0}, \
{GNB_CONFIG_STRING_ENABLE_SDAP, GNB_CONFIG_HLP_STRING_ENABLE_SDAP, PARAMFLAG_BOOL,.iptr=NULL, .defintval=1, TYPE_INT, 0}, \
{GNB_CONFIG_STRING_GNB_DU_ID, GNB_CONFIG_HLP_GNB_DU_ID, 0, .u64ptr=NULL, .defint64val=1, TYPE_UINT64, 0}, \
{GNB_CONFIG_STRING_GNB_CU_UP_ID, GNB_CONFIG_HLP_GNB_CU_UP_ID, 0, .u64ptr=NULL, .defint64val=1, TYPE_UINT64, 0}, \
{GNB_CONFIG_STRING_USE_DELTA_MCS, GNB_CONFIG_HLP_USE_DELTA_MCS, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
@@ -219,21 +216,20 @@ typedef enum {
#define GNB_UMONDEFAULTDRB_IDX 23
#define GNB_FORCE256QAMOFF_IDX 24
#define GNB_ENABLE_SDAP_IDX 25
#define GNB_DRBS 26
#define GNB_GNB_DU_ID_IDX 27
#define GNB_GNB_CU_UP_ID_IDX 28
#define GNB_USE_DELTA_MCS_IDX 29
#define GNB_FORCEUL256QAMOFF_IDX 30
#define GNB_MAXMIMOLAYERS_IDX 31
#define GNB_DISABLE_HARQ_IDX 32
#define GNB_NUM_DL_HARQ_IDX 33
#define GNB_NUM_UL_HARQ_IDX 34
#define GNB_UESS_AGG_LEVEL_LIST_IDX 35
#define GNB_CU_SIBS_IDX 36
#define GNB_DU_SIBS_IDX 37
#define GNB_CONFIG_REP_IDX 38
#define GNB_1ST_ACTIVE_BWP_IDX 39
#define GNB_LIMIT_RSRP_REPORT_IDX 40
#define GNB_GNB_DU_ID_IDX 26
#define GNB_GNB_CU_UP_ID_IDX 27
#define GNB_USE_DELTA_MCS_IDX 28
#define GNB_FORCEUL256QAMOFF_IDX 29
#define GNB_MAXMIMOLAYERS_IDX 30
#define GNB_DISABLE_HARQ_IDX 31
#define GNB_NUM_DL_HARQ_IDX 32
#define GNB_NUM_UL_HARQ_IDX 33
#define GNB_UESS_AGG_LEVEL_LIST_IDX 34
#define GNB_CU_SIBS_IDX 35
#define GNB_DU_SIBS_IDX 36
#define GNB_CONFIG_REP_IDX 37
#define GNB_1ST_ACTIVE_BWP_IDX 38
#define GNB_LIMIT_RSRP_REPORT_IDX 39
#define TRACKING_AREA_CODE_OKRANGE {0x0001,0xFFFD}
#define NUM_DL_HARQ_OKVALUES {2,4,6,8,10,12,16,32}
@@ -272,7 +268,6 @@ typedef enum {
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s1 = { config_check_intval, NUM_DL_HARQ_OKVALUES,8 } }, \
{ .s1 = { config_check_intval, NUM_UL_HARQ_OKVALUES,2 } }, \
{ .s5 = { NULL } }, \
+5 -2
View File
@@ -60,8 +60,11 @@ static void fill_DRB_configList_e1(NR_DRB_ToAddModList_t *DRB_configList, const
asn1cCalloc(ie->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++) {
+8
View File
@@ -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;
+1
View File
@@ -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
+117 -78
View File
@@ -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;
}
+115 -86
View File
@@ -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;
}
+1 -1
View File
@@ -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);
+2
View File
@@ -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));
+4 -6
View File
@@ -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);
}
+3 -1
View File
@@ -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);