QoS handling: add 5QI validation during QoS flow setup/modify

Move 5QI validation from RRC reconfiguration message generation to
where QoS flows are actually added/updated, ensuring validation
happens early in the process.

Changes:
- Remove redundant 5QI validation from rrc_gNB_modify_dedicatedRRCReconfiguration():
  that was checking values right before RRC message generation
- Add 5QI validation in add_qos() to reject unsupported 5QI values
  during PDU session setup
- Add 5QI validation in nr_rrc_update_qos to skip unsupported 5QI
  values during PDU session modify (continues to next flow)
- Add is_5qi_supported() function in rrc_gNB_radio_bearers.c:
  validates standardized 5QI values (1-9, 65-90) per 3GPP TS 23.501
  Table 5.7.4-1 and checks against the classification map

Also, remove remaining limit to 1 QoS flow per DRB.

Closes #541

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
This commit is contained in:
Guido Casati
2025-11-07 19:24:14 +01:00
parent 8ceb7ec37b
commit 04126759aa
5 changed files with 68 additions and 37 deletions

View File

@@ -42,6 +42,7 @@ typedef enum { NON_DYNAMIC, DYNAMIC } fiveQI_t;
* - Pre-configured 5QI values: pre-configured in the AN
* - Dynamically assigned 5QI values: require signaling of QoS characteristics as part of QoS profile */
#define MIN_FIVEQI 0
#define MAX_STANDARDIZED_FIVEQI 90
#define MAX_FIVEQI 255
/* ARP Priority Level - 3GPP TS 23.501 §5.7.2.2

View File

@@ -1007,43 +1007,15 @@ static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_
void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
{
int qos_flow_index = 0;
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue_p, 0, false);
ue_p->xids[params.transaction_id] = RRC_PDUSESSION_MODIFY;
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) {
pdusession_t *session = &item->param;
item->xid = params.transaction_id;
// bypass the new and already configured pdu sessions
if (item->status != PDU_SESSION_STATUS_TOMODIFY) {
continue;
}
// Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) {
switch (qos->qos.fiveQI) {
case 1: //100ms
case 2: //150ms
case 3: //50ms
case 4: //300ms
case 5: //100ms
case 6: //300ms
case 7: //100ms
case 8: //300ms
case 9: //300ms Video (Buffered Streaming)TCP-based (e.g., www, e-mail, chat, ftp, p2p file sharing, progressive video, etc.)
// TODO
break;
default:
LOG_E(NR_RRC, "not supported 5qi %lu\n", qos->qos.fiveQI);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CauseRadioNetwork_not_supported_5QI_value};
item->status = PDU_SESSION_STATUS_FAILED;
item->xid = params.transaction_id;
item->cause = cause;
continue;
}
LOG_I(NR_RRC, "PDU Session ID %d, QOS flow %d, 5QI %ld \n", session->pdusession_id, qos_flow_index, qos->qos.fiveQI);
}
}
byte_array_t msg = do_RRCReconfiguration(&params);

View File

@@ -934,13 +934,30 @@ static void nr_rrc_update_qos(seq_arr_t *list, const int nb_qos, const pdusessio
DevAssert(nb_qos == 1);
DevAssert(nb_qos < MAX_QOS_FLOWS);
for (uint8_t i = 0; i < nb_qos; ++i) {
nr_rrc_qos_t *qos = find_qos(list, in_qos[i].qfi);
const pdusession_level_qos_parameter_t *q_in = &in_qos[i];
// Validate QFI range (0-63)
if (q_in->qfi < 0 || q_in->qfi > 63) {
LOG_E(NR_RRC, "QoS flow QFI=%d: Invalid QFI (must be 0-63). Skipping QoS flow.\n", q_in->qfi);
continue;
}
// Validate 5QI value
if (!is_5qi_standardized(q_in->fiveQI)) {
LOG_W(NR_RRC,
"QoS flow QFI=%d: 5QI %lu is not a standardized value (1-9, 65-90). Skipping QoS flow.\n",
q_in->qfi,
q_in->fiveQI);
continue;
}
nr_rrc_qos_t *qos = find_qos(list, q_in->qfi);
if (qos) {
AssertFatal(qos->qos.qfi == in_qos[i].qfi, "QoS Flow to modify must match existing one");
LOG_I(NR_RRC, "Updating QoS for QFI=%d\n", in_qos[i].qfi);
qos->qos = in_qos[i];
AssertFatal(qos->qos.qfi == q_in->qfi, "QoS Flow to modify must match existing one");
LOG_I(NR_RRC, "Updating QoS for QFI=%d\n", q_in->qfi);
qos->qos = *q_in;
} else {
LOG_E(NR_RRC, "Failed to update QoS for QFI=%d: QoS flow not found\n", in_qos[i].qfi);
LOG_E(NR_RRC, "Failed to update QoS for QFI=%d: QoS flow not found\n", q_in->qfi);
}
}
}

View File

@@ -48,6 +48,26 @@ nr_rrc_qos_t *add_qos(seq_arr_t *qos, const pdusession_level_qos_parameter_t *in
return NULL;
}
// Validate QFI range (0-63)
if (in->qfi > 63) {
LOG_E(NR_RRC, "Invalid QFI=%d: QFI must be in range 0-63. Skipping QoS flow.\n", in->qfi);
return NULL;
}
// TS 23.501: "The QFI shall be unique within a PDU Session."
// If QFI already exists, keep the existing mapping to avoid ambiguity.
nr_rrc_qos_t *existing = find_qos(qos, in->qfi);
if (existing) {
LOG_W(NR_RRC, "Duplicate QoS flow QFI=%d for the same PDU session; skipping add (keeping existing)\n", in->qfi);
return existing;
}
// Validate 5QI value (TS 23.501 allows dynamically assigned 5QIs)
if (in->fiveQI_type == NON_DYNAMIC && !is_5qi_standardized(in->fiveQI)) {
LOG_W(NR_RRC, "QoS flow QFI=%d: 5QI %ld is not a standardized value. Skipping QoS flow.\n", in->qfi, in->fiveQI);
return NULL;
}
nr_rrc_qos_t item = {.qos = *in};
seq_arr_push_back(qos, &item, sizeof(nr_rrc_qos_t));
@@ -56,9 +76,6 @@ nr_rrc_qos_t *add_qos(seq_arr_t *qos, const pdusession_level_qos_parameter_t *in
DevAssert(added);
LOG_I(NR_RRC, "Added QoS flow with qfi=%d, total number of QoS flows = %ld\n", in->qfi, seq_arr_size(qos));
// Only one QoS flow is supported
AssertFatal(seq_arr_size(qos) == 1, "only 1 Qos flow supported\n");
return added;
}
@@ -81,6 +98,12 @@ bool rm_qos(seq_arr_t *flows, int qfi)
{
DevAssert(flows);
// Validate QFI range (0-63)
if (qfi < 0 || qfi > 63) {
LOG_E(NR_RRC, "Invalid QFI=%d: QFI must be in range 0-63. Skipping QoS flow release.\n", qfi);
return false;
}
nr_rrc_qos_t *qos = find_qos(flows, qfi);
if (!qos) {
LOG_W(NR_RRC,
@@ -345,6 +368,21 @@ static const qos_resource_type_t five_qi_resource_type[MAX_STANDARDIZED_FIVEQI +
[90] = QOS_RESOURCE_TYPE_DC_GBR,
};
/** @brief Check if 5QI value is a standardized value (table 5.7.4-1 in 3GPP TS 23.501)
* @param five_qi 5QI value to check
* @return true if 5QI is standardized value (1-9, 65-90), false otherwise */
bool is_5qi_standardized(uint16_t five_qi)
{
DevAssert(five_qi <= MAX_FIVEQI);
// TS 23.501 Table 5.7.4-1: standardized 5QI values
return (five_qi >= 1 && five_qi <= 10) || // GBR / Non-GBR
(five_qi >= 65 && five_qi <= 67) || // GBR
(five_qi >= 69 && five_qi <= 70) || // Non-GBR
(five_qi >= 71 && five_qi <= 76) || // GBR
(five_qi >= 79 && five_qi <= 80) || // Non-GBR
(five_qi >= 82 && five_qi <= MAX_STANDARDIZED_FIVEQI); // DC-GBR
}
// Design-specific DRB multiplexing limits per resource type
#define MAX_QOS_FLOWS_PER_DRB_DC_GBR 1 // Strict isolation for delay-critical GBR
#define MAX_QOS_FLOWS_PER_DRB_GBR 2 // Conservative multiplexing for GBR
@@ -472,7 +510,7 @@ int nr_rrc_find_suitable_drb_for_qos(gNB_RRC_UE_t *UE,
if (has_capacity && total_flows_on_drb < MAX_QOS_FLOWS_PER_DRB_TOTAL) {
LOG_I(NR_RRC,
"Found suitable DRB %d to multiplex 5QI %lu (QFI %d) - current flows: GBR=%d, Non-GBR=%d\n",
"Found suitable DRB %d to multiplex 5QI %ld (QFI %d) - current flows: GBR=%d, Non-GBR=%d\n",
drb->drb_id,
qos_params->fiveQI,
qos_params->qfi,

View File

@@ -63,4 +63,7 @@ int nr_rrc_find_suitable_drb_for_qos(gNB_RRC_UE_t *UE,
const pdusession_level_qos_parameter_t *qos_params,
const seq_arr_t *flows);
/// @brief Check if 5QI value is a standardized value (table 5.7.4-1 in 3GPP TS 23.501)
bool is_5qi_standardized(uint16_t five_qi);
#endif