Compare commits

...

27 Commits

Author SHA1 Message Date
Guido Casati
e655a4ff2d WIP Add tests for RRC radio bearers lib
* TODO find better way to write the CMakeLists.c
2025-07-04 10:24:20 +02:00
Guido Casati
d94d2e3311 Remove irrelevant ASN1 include in rrc_gNB_radio_bearers.h 2025-07-04 10:24:20 +02:00
Guido Casati
5802bc61c1 WIP Refactor NAS PDU handling in RRC/NGAP
* Replace single nas_pdu with seq_arr list in gNB_RRC_UE_t
* Add NAS PDUs from PDU Session items to the same list
* NAS PDU in Downlink NAS Transport, NGAP InitialContextSetup,
  PDU Session Setup/Modify is forwarded directly in RRC DLInformationTransfer
  when conditions apply
* Forward stored NAS PDU in list with rrc_forward_stored_nas_pdus()
* Store NAS PDUs from NGAP InitialContextSetup,
  PDU Session Setup/Modify when they need to be forwarded in a second stage
* Simplify pdusession_t: remove nas_pdu field and deep copy logic

Complies with TS 38.413 and TS 38.331 for transparent NAS handling
2025-07-04 10:24:20 +02:00
Guido Casati
0c1f33a664 WIP TODOs 2025-07-04 10:24:20 +02:00
Guido Casati
252f93a52b Look up active PDU session (in setup/addmod list) in bearer context modification
During re-establishment the PDUs linked to the DRBs to be re-established
are already in the "setup" list, therefore the logic was extended to find
the "active" pdu session linked to the DRB, first in the addmod list,
then if not found there in the setup list.
2025-07-04 10:24:20 +02:00
Guido Casati
2185c72adf Documentation related to PDUSession and DRB setup/modify/release in E1AP procedures 2025-07-04 10:24:20 +02:00
Guido Casati
1caeb4e525 Cleanup unused defines in openair2/RRC/NR/rrc_gNB_radio_bearers.h 2025-07-04 10:24:20 +02:00
Guido Casati
5ad8a14651 Release DRBs per PDU session in NGAP Release Response
* Introduced find_drb() and remove_drbs_by_pdu_session() helpers to locate
  and clean up DRBs linked to a specific PDU session
* Targeted per-session DRB removal in rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE()
* Ensures DRBs for unaffected PDU sessions remain intact
2025-07-04 10:24:20 +02:00
Guido Casati
e20a225172 Adopt release_pduSessions in handle_rrcReconfigurationComplete
After the RRCReconfiguration for PDU Session Release, the NG(-C) tunnels
on the N2 interface need to be deleted for the released PDU Sessions
2025-07-04 10:24:20 +02:00
Guido Casati
d61877b7e1 Add release_pduSessions to release tunnels on the N3 interface 2025-07-04 10:24:20 +02:00
rmagueta
ef270b47ce Added missing procedures for PDUSession Release
Implement end-to-end DRB and PDU session release handling across RRC, PDCP, F1AP, E1AP

Key changes:

RRC:
* `rrc_gNB_generate_dedicatedRRCReconfiguration_release()` now sends both the
  RRCReconfiguration message and triggers UE Context Modification to DU via F1AP.
* Introduced `deliver_ue_ctxt_modification_data_t` and
  `rrc_deliver_ue_ctxt_modif_req()` for proper delivery of RRC container to MAC.

NGAP:

In `rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND()`:
* Populates E1AP Bearer Modification for each released session.
* If a CU-UP is connected, triggers `bearer_context_mod()` for PDU teardown.

PDCP:
* Added `nr_pdcp_release_drbs()` to remove all DRBs linked to a given PDU session.
* Called from `e1_bearer_context_modif()` for correct clean-up during session release.

GTP-U:
* GTP tunnel teardown now handled in `e1_bearer_context_modif()` using the correct
  F1 or N3 GTP instance (`newGtpuDeleteOneTunnel()`).

MAC:
* Removed old GTP tunnel teardown from MAC RLC handler (`handle_ue_context_drbs_release`),
  now handled entirely by PDCP/E1AP (Bearer Context Modification Response)

Clean-up:
* Removed duplicated definition of `deliver_ue_ctxt_modification_data_t` in mobility code.
* Ensured xid tracking works consistently between PDU Session Establishment (with NAS PDU)
  and dedicated RRCReconfiguration

Co-authored-by: Guido Casati <hello@guidocasati.com>
2025-07-04 10:24:20 +02:00
Guido Casati
700171fbef Add telnet CI command to trigger NGAP PDU Session Release
This commit introduces a new telnet server command `pdu_session_release` to
manually trigger the NGAP PDU Session Release Command message from the telnet ci shell.
This is useful for integration testing of PDU session teardown behavior.

Details:
- Added `trigger_ngap_pdu_session_release()` to send NGAP_PDUSESSION_RELEASE_COMMAND
  to the RRC task, targeting the specified `gNB_ue_ngap_id`.
- The command currently releases PDU session ID 10 (hardcoded).
- Hooked into telnet shell with command: `pdu_session_release [gNB_ue_ngap_id]`

Usage example:
telnet > pdu_session_release 1
2025-07-04 10:24:20 +02:00
rmagueta
14401f81b8 Add PDU Session Resource To Remove List according to TS 38.463 - clause 9.2.2.4 and 9.3.3.12 2025-07-04 10:24:20 +02:00
Guido Casati
48a608494b Fix DRB release logic: properly release DRBs associated with PDU sessions
Previously, rrc_gNB_generate_dedicatedRRCReconfiguration_release() used a naive
DRB ID counter to determine which DRBs to release, assuming a 1:1 mapping
between DRB list order and DRB ID. This approach was incorrect in scenarios
where DRBs were added or removed dynamically.

This patch fixes the issue by explicitly matching each PDU session ID
in the release list with its corresponding DRBs in the UE context.
Only DRBs associated with the given PDU session are added to the
DRB_Release_configList.

Key changes:
* Replaced static DRB ID generation with lookup of actual (drb_t, drb_id) values
* Ensures correct DRB release behavior during RRC Reconfiguration
2025-07-04 10:24:20 +02:00
Guido Casati
a5df4b4892 Refactor DRB configuration in RRC and ASN.1 to reduce duplication and simplify logic
This commit consolidates DRB configuration logic into reusable helper functions,
removing duplicated handling between internal RRC and ASN.1 DRB generation. The result
is cleaner, more maintainable DRB setup across RRC and E1AP, and clear separation
between internal RRC config and ASN.1 structs.

The legacy function `generateDRB(...)` updated the internal drb_t structure within
`gNB_RRC_UE_t` and `generateDRB_ASN1(...)` translated the internal drb_t into the
ASN.1 representation (`NR_DRB_ToAddMod_t`). Both functions duplicated a
lot of logic, with multiple places to maintain consistently.

Highlights:

Centralized ASN.1 DRB generation:
* Added `get_DRB_ToAddMod()` to construct NR_DRB_ToAddMod_t from internal data, the
* Added `get_default_PDCP_config()` to encapsulate PDCP config ASN.1 logic
* Added `get_sdap_config()` to build SDAP config from PDU session info
* Added `setup_rrc_drb_for_pdu_session()` to setup DRB in the RRC seq_arr (DRB setup)

Simplified E1 bearer context PDCP config:
* Refactored `set_bearer_context_pdcp_config()` to take default PDCP and UM mode flag,
  and return `bearer_context_pdcp_config_t` by value

Removed legacy, duplicated functions:
* Deleted `generateDRB()`, `generateDRB_ASN1()`, `get_next_available_drb_id()`
* Removed embedded SDAP/PDCP config structures from `drb_t`: simpler DRB representation,
  internal drb_t struct stays clean and minimal

Updated RRC DRB processing:
* RRC now uses `get_DRB_ToAddMod()` directly in `createDRBlist()`
* DRB identity and QoS flows now fetched via clean abstractions: DRB IDs are assigned
  sequentially using setup_rrc_drb_for_pdu_session() with internal capacity checks,
  while QoS flow mappings (QFIs) are encapsulated in the `new nr_sdap_configuration_t`
  structure, built via `get_sdap_config()` from the PDU session state.

Assumptions:
* Each DRB maps to the same QoS flow, in line with existing assumptions
* QoS configuration is stored in pdusession_t
* SDAP config is default-enabled
* DRB ID is assigned sequentially, capped by MAX_DRBS_PER_UE
2025-07-04 10:24:19 +02:00
Guido Casati
edc19c6110 Refactor DRB handling using dynamic seq_arr structure
The previous DRB management relied on a fixed-size array (established_drbs[MAX_DRBS_PER_UE])
and manual status tracking using DRB_ACTIVE/INACTIVE flags. This approach was inefficient,
and made assumptions about DRB ID allocation that do not generalize well
to dynamic procedures like bearer modification or removal.

* Replace fixed-size array established_drbs[] with dynamic
  seq_arr_t *drbs in gNB_RRC_UE_t
* Implemented get_drb() and add_rrc_drb() with clean
  abstractions using find_if()
* Updated all DRB-related logic (e.g., generateDRB,
  F1AP/E1AP tunnel config, reestablishment, mobility) to use seq_arr.
* Removed status field and DRB_ACTIVE/INACTIVE logic, allocation
  now tracked via seq_arr size
* Simplifies DRB management and avoids DRB ID–indexed assumptions: code assumed
  DRB IDs mapped directly to array indices: established_drbs[drb_id - 1]
  This could break when: DRB IDs are not sequential, DRBs are removed and re-added,
  or there are gaps in IDs
2025-07-02 11:36:39 +02:00
Guido Casati
5597f8281e Refactor PDU Session Handling State Machine
This commit introduces a redesigned approach to managing PDU Sessions in the gNB RRC layer,
with a focus on simplifying state tracking and improving clarity in control flow.

The previous PDU Session handling logic mixed runtime state and NGAP procedures
in a single structure. This tightly coupled design made it difficult to track the lifecycle
of each session and error-prone to maintain. Control flow was scattered, and there was no
clear separation between sessions being created, modified, failed, or released.
As a result, extending support for multiple concurrent sessions became unnecessarily complex.

Key Changes:

1. Removed internal status tracking:
   The previous approach relied on implicit status fields to track the state of each PDU Session,
   which led to complex logic and hard-to-maintain code paths. These internal flags have been
   removed in favor of a more explicit and structured design.

2. Replaced with sequenced-array–based lists:
   PDU Sessions are now managed explicitly through four dedicated lists (seq_arr), each representing
   a distinct stage of the session lifecycle:

   - setup: active PDU Sessions that are established and associated with DRBs
   - addmod: PDU Sessions to be added or modified as part of the current procedure (e.g. RRCReconfiguration)
   - failed: PDU Sessions for which the NG signaling procedure (e.g., setup or modify) failed
   - release: PDU Sessions that are pending release and need to be deleted

This approach provides clearer tracking of PDU Session items during NGAP and RRC procedures
without the need of state transitions. To this purpose:

* Removed obsolete session status enums and related switch cases
* Centralized allocation and cleanup logic for session memory
* Added add/update/remove logic to manipulate the lists

Tested Scenarios:

* Single PDU Session Establishment flows
* Correct delivery of NAS PDU in RRCReconfiguration
* RRCReconfigurationComplete triggers appropriate NGAP response

This refactor is the ground base for the future multi-session scenarios.
2025-07-02 11:36:38 +02:00
Guido Casati
c0f284319a Simplify signature of get_qos_characteristics
Use directly pdusession_t, this will simplify the
refactoring of PDU Session handling
2025-07-02 11:30:21 +02:00
Guido Casati
39e670286a Refactor: Centralize QoS structures to eliminate cross-layer duplication and dependencies
This commit unifies the QoS-related data structures (`qos_characteristics_t`,
`ngran_allocation_retention_priority_t`, and `qos_flow_level_qos_parameters_t`)
into a common platform header (`5g_platform_types.h`), and removes redundant,
layer-specific definitions across E1AP, F1AP, NGAP, and RRC.

Before this commit the stack had different definitions for QoS Flow Level QoS Parameters
and they were basically repeated across NGAP, E1AP and F1AP. This commit is moving the
definition to platform types so that it can be reused by different modules. This also
simplify transfer of data between different module, through shallow copies.

Key improvements:
* Eliminates F1AP–RRC cross-dependency by removing F1AP-specific QoS types from RRC logic
* Ensures consistent and standard-compliant QoS parameter usage across the stack
* Simplifies encode/decode, equality, and mapping functions between layers: all QoS
  encoding/decoding logic (fill_qos, f1ap_write_drb_qos_param, etc.)
  now uses the shared qos_flow_level_qos_parameters_t.
* Enables cleaner modular design and better maintainability
* Reduces risk of misalignment across protocol layers

Also:

* Fixes incorrect field assignments in F1AP DU-side decode logic
  (i.e., preemption capability vs vulnerability): f1ap_du_ue_context_management.c,
  preemption_capability was mistakenly set to pre_emptionVulnerability
* Replaces verbose field names with acronyms (i.e., 'arp')
  alloc_reten_priority to arp: in qos_flow_level_qos_parameters_t,
  the struct member alloc_reten_priority is renamed to arp,
  (acronym (Allocation and Retention Priority) used in 3GPP specs).
  This makes the code more readable.
2025-07-02 11:30:21 +02:00
Guido Casati
18279503d0 Split find_pduSession in 2 functions, to add and find the PDU session
* find_pduSession shall only fetch the PDU Session by its ID
* add_pduSession shall only create a new PDU session with the provided
  configuration and add it to the stored list of PDU sessions
2025-07-02 11:30:20 +02:00
Guido Casati
a18e2961f9 Adopt seq_arr in PDU Session handling
The RRC PDU Session struct is converted from a fixed-size array
to a dynamically sized seq_arr. This improves flexibility and
simplifies session management by eliminating arbitrary size limits
and enabling dynamic addition/removal of PDU sessions.

Helper functions and loops have been adapted to use the seq_arr API.
This change is a step toward more robust and maintainable handling
of PDU sessions in the gNB RRC layer.

The "status" state machine remains untouched in this commit and
is refactored in a later commit.

Also:

* add macro to push to seq_arr and return the new element
* move initialization of xid (RRC PDU Session struct) to
  find_pduSession (temporary, will be finalized in a later
  commit) since with the new seq_arr design the initialization
  is no longer possible in the UE context generation (also
  does not belong to it, initialize when adding a new PDU Session)
2025-07-01 14:50:59 +02:00
Guido Casati
e74a071958 Add macro to cleanup and free seq_arr
seq_arr can be used for temporary list, therefore its instances
can be freed after use. In order to do so these steps are performed in the macro:

* Fully deallocate a sequence array (seq_arr_t) with seq_arr_free
* Free and NULL the seq_arr_t struct itself
2025-07-01 11:20:04 +02:00
Guido Casati
1ba413cd2d Check pointer allocation before executing loop in FOR_EACH_SEQ_ARR
* The pointer can be NULL and it that case the loop is not executed
2025-07-01 11:18:57 +02:00
Guido Casati
e939d0c9b0 Return 0 size in seq_arr_size instead of asserting
The returned size is generally used to loop through
the seq_arr, in the current code, it is used in 2 scenarios:
(1) for loop (2) qsort. Both these iterations will be skipped
if size is 0, so seq_arr NULL.

seq_arr: Return 0 in seq_arr_size() when input is NULL

Instead of asserting, return 0 when the input `seq_arr_t*` is NULL.
This improves robustness in calling code, where `seq_arr_size()` is
used in safe contexts like `for` loops and `qsort`, both of which
gracefully handle a size of 0.

This change avoids unnecessary crashes due to NULL pointer access
in non-critical paths, e.g. in the upcoming commits, when looping
through the PDU Session setup list, before the list is initialised.
2025-06-27 15:08:11 +02:00
Guido Casati
3bd14be930 "find" library helpers: make inputs const, return {found=false} on failure
The find lib functions are returing an elm_arr_t type struct
which is containing a "found" bool member, which can be use to
signal externally that no element was found. This avoid asserting
on the seq_arr being equal to NULL, which is already checked by
seq_arr lib functions. This change is necessary since the find
lib will be adopted in PDU Session handling and it may happens that
a find function is called when the PDU Session seq_arr is not
initialized yet, leading to an assert.

This commit:

* Mark read input parameters to find helpers (find_if, find_if_arr_it) as const
* Ensure failures return a {found = false, it = NULL} element
2025-06-27 14:55:40 +02:00
Guido Casati
86ce851b9f Fix incosistency in nr_sdap_delete_entity
The function was printing a "not found" error message even in the case
the return value was set to "true". Return directly true/false.
2025-06-26 17:31:42 +02:00
Guido Casati
0f7aed20fe Add free_pdusession to be used with different types of PDU Session lists (toaddmod,setup,remove) 2025-06-26 17:31:42 +02:00
37 changed files with 1503 additions and 924 deletions

View File

@@ -55,4 +55,41 @@ typedef enum {
typedef enum { NON_DYNAMIC, DYNAMIC } fiveQI_t;
typedef struct {
union {
struct {
uint16_t fiveqi;
uint8_t qos_priority_level;
} non_dynamic;
struct {
// Range 5QI [0 - 255]
uint16_t fiveqi;
/* Range [0 - 15]
15 = "no priority," 1-14 = decreasing priority (1 highest), 0 = logical error if received */
uint8_t qos_priority_level;
// Range [0, 1023]: Upper bound for packet delay in 0.5ms units
uint16_t packet_delay_budget;
struct {
// PER = Scalar x 10^-k (k: 0-9)
uint8_t per_scalar;
uint8_t per_exponent;
} packet_error_rate;
} dynamic;
};
fiveQI_t qos_type;
} qos_characteristics_t;
typedef struct {
uint16_t priority_level;
// Pre-emption capability on other QoS flows
uint8_t preemption_capability;
// Vulnerability of the QoS flow to pre-emption of other QoS flows
uint8_t preemption_vulnerability;
} ngran_allocation_retention_priority_t;
typedef struct {
qos_characteristics_t qos_characteristics;
ngran_allocation_retention_priority_t arp;
} qos_flow_level_qos_parameters_t;
#endif

View File

@@ -27,22 +27,28 @@ SOFTWARE.
#include <stdlib.h>
#include <stdio.h>
elm_arr_t find_if_arr_it(seq_arr_t* arr, void* start_it, void* end_it, void* value, bool (*f)(const void*, const void*))
#define CHECK_NULL_AND_RETURN(x) \
do { \
if ((x) == NULL) \
return (elm_arr_t){.found = false, .it = NULL}; \
} while (0)
elm_arr_t find_if_arr_it(const seq_arr_t* arr, void* start_it, void* end_it, const void* value, bool (*f)(const void*, const void*))
{
assert(arr != NULL);
CHECK_NULL_AND_RETURN(arr);
while (start_it != end_it) {
if (f(value, start_it))
return (elm_arr_t){.found = true, .it = start_it};
start_it = seq_arr_next(arr, start_it);
}
// If I trusted the average developer I should return it=start_it, but I don't.
return (elm_arr_t){.found = false, .it = NULL};
}
elm_arr_t find_if(seq_arr_t* arr, void* value, bool (*f)(const void*, const void*))
elm_arr_t find_if(const seq_arr_t* arr, const void* value, bool (*f)(const void*, const void*))
{
assert(arr != NULL);
CHECK_NULL_AND_RETURN(arr);
void* start_it = seq_arr_front(arr);
void* end_it = seq_arr_end(arr);
return find_if_arr_it(arr, start_it, end_it, value, f);

View File

@@ -42,7 +42,7 @@ typedef struct {
* @param f Function representing the predicate
* @return Whether the predicate was fullfilled and the iterator to the element if true
*/
elm_arr_t find_if(seq_arr_t* arr, void* value, bool (*f)(const void* value, const void* it));
elm_arr_t find_if(const seq_arr_t* arr, const void* value, bool (*f)(const void* value, const void* it));
/**
* @brief Find elements in an array in the semi-open range [start_it, end_it)
@@ -53,6 +53,6 @@ elm_arr_t find_if(seq_arr_t* arr, void* value, bool (*f)(const void* value, cons
* @param f Function representing the predicate
* @return Whether the predicate was fullfilled and the iterator to the element if true
*/
elm_arr_t find_if_arr_it(seq_arr_t* arr, void* start_it, void* end_it, void* value, bool (*f)(const void* value, const void* it));
elm_arr_t find_if_arr_it(const seq_arr_t* arr, void* start_it, void* end_it, const void* value, bool (*f)(const void* value, const void* it));
#endif

View File

@@ -146,7 +146,8 @@ void seq_arr_erase_it(seq_arr_t* arr, void* start_it, void* end_it, void (*free_
size_t seq_arr_size(seq_arr_t const* arr)
{
assert(arr != NULL);
if (!arr)
return 0;
return arr->size;
}

View File

@@ -35,7 +35,34 @@ SOFTWARE.
#include <stdint.h>
#define FOR_EACH_SEQ_ARR(type, var, arr) \
for (type var = seq_arr_front(arr); var != seq_arr_end(arr); var = seq_arr_next(arr, var))
if ((arr) != NULL) \
for (type var = seq_arr_front(arr); var != seq_arr_end(arr); var = seq_arr_next(arr, var))
#define SEQ_ARR_CLEANUP_AND_FREE(seq_ptr, free_func) \
do { \
if ((seq_ptr) != NULL) { \
seq_arr_free((seq_ptr), (free_func)); \
free(seq_ptr); \
seq_ptr = NULL; \
} \
} while (0)
#define SEQ_ARR_PUSH_BACK_AND_GET(type, arr, data_ptr) \
(seq_arr_push_back((arr), (data_ptr), sizeof(type)), \
((type *)seq_arr_end((arr)) - 1))
/** @brief Initializes a seq_arr of type @param elt_type and maximum size @param size */
#define SEQ_ARR_INIT(pp, elt_type, max_size) \
do { \
seq_arr_t **_pp = (pp); \
if (*_pp == NULL) { \
*_pp = malloc_or_fail(sizeof(**_pp)); \
seq_arr_init(*_pp, sizeof(elt_type)); \
} \
if (seq_arr_size(*_pp) >= (max_size)) { \
return NULL; \
} \
} while (0)
typedef struct seq_arr_s {
uint8_t* data;

View File

@@ -38,6 +38,7 @@
#include "openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h"
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
#include "openair3/NGAP/ngap_gNB_ue_context.h"
#define TELNETSERVERCODE
#include "telnetsrv.h"
@@ -215,6 +216,23 @@ int force_ue_release(char *buf, int debug, telnet_printfunc_t prnt)
return 0;
}
int trigger_ngap_pdu_session_release(char *buf, int debug, telnet_printfunc_t prnt)
{
int gNB_ue_ngap_id = (buf != NULL) ? strtol(buf, NULL, 10) : 1;
ngap_gNB_ue_context_t *ngap = ngap_get_ue_context(gNB_ue_ngap_id);
if (ngap == NULL)
ERROR_MSG_RET("no NGAP UE context for gNB_ue_ngap_id %d\n", gNB_ue_ngap_id);
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PDUSESSION_RELEASE_COMMAND);
ngap_pdusession_release_command_t * msg = &NGAP_PDUSESSION_RELEASE_COMMAND(message_p);
memset(msg, 0, sizeof(*msg));
msg->amf_ue_ngap_id = ngap->amf_ue_ngap_id;
msg->gNB_ue_ngap_id = ngap->gNB_ue_ngap_id;
msg->nb_pdusessions_torelease = 1;
msg->pdusession_release_params->pdusession_id = 10;
itti_send_msg_to_task(TASK_RRC_GNB, 0, message_p);
return 0;
}
static telnetshell_cmddef_t cicmds[] = {
{"get_single_rnti", "", get_single_rnti},
{"force_reestab", "[rnti(hex,opt)]", trigger_reestab},
@@ -223,6 +241,7 @@ static telnetshell_cmddef_t cicmds[] = {
{"force_ul_failure", "[rnti(hex,opt)]", force_ul_failure},
{"trigger_f1_ho", "[rrc_ue_id(int,opt)]", rrc_gNB_trigger_f1_ho},
{"fetch_du_by_ue_id", "[rrc_ue_id(int,opt)]", fetch_du_by_ue_id},
{"pdu_session_release", "[gNB_ue_ngap_id(int,opt)]", trigger_ngap_pdu_session_release},
{"", "", NULL},
};

View File

@@ -72,3 +72,196 @@ sequenceDiagram
u->>c: BEARER CONTEXT MODIFICATION RESPONSE
Note over c: e1apCUCP_handle_BEARER_CONTEXT_MODIFICATION_RESPONSE
```
## DRB Setup over E1
```mermaid
sequenceDiagram
participant Network
participant CUCP
participant CUUP
participant DU
participant UE
Network->>CUCP: NGAP_PDUSESSION_SETUP_REQ
Note over CUCP: ngap_gNB_handle_pdusession_setup_request
CUCP->>CUCP: decodePDUSessionResourceSetup
CUCP->>CUCP: rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ
Note over CUCP: cp_pdusession_resource_item_to_pdusession
CUCP->>CUCP: E1AP: trigger_bearer_setup
Note over CUCP: fill Bearer Context Setup Request
loop nb_pdusessions_tosetup
CUCP->>CUCP: add_pduSession (&UE->pduSessions_to_addmod)
loop numDRB2Setup
CUCP->>CUCP: setup_rrc_drb_for_pdu_session
Note over CUCP: fill DRB ID, PDU Session, QoS in RRC DRB
CUCP->>CUCP: add_rrc_drb
CUCP->>CUCP: fill_drb_ngran_tosetup
Note over CUCP: fill SDAP, PDCP, QoS in E1 message
end
end
Note over CUCP: e1_bearer_context_setup
CUCP->>CUUP: BEARER CONTEXT SETUP REQUEST
Note over CUUP: e1apCUUP_handle_BEARER_CONTEXT_SETUP_REQUEST
Note over CUUP: set up DRBs, PDCP/SDAP entities, GTP tunnel
CUUP->>CUCP: E1AP_BEARER_CONTEXT_SETUP_RESP
Note over CUCP: rrc_gNB_process_e1_bearer_context_setup_resp
loop numPDUSessions
CUCP->>CUCP: find_pduSession (&UE->pduSessions_to_addmod)
Note over CUCP: Update N3 tunnel info in pdusession_t
loop numDRBSetup
CUCP->>CUCP: get_drb (from RRC)
Note over CUCP: update DRB F1 tunnel info
end
end
CUCP->>DU: F1 UE Context Setup Request / ue_context_modification_request
DU->>CUCP: F1 UE Context Setup Response
CUCP->>CUCP: rrc_CU_process_ue_context_setup_response / rrc_CU_process_ue_context_modification_response
Note over CUCP: e1_send_bearer_updates
CUCP->>CUUP: E1 BEARER CONTEXT MODIFICATION REQUEST
CUUP->>CUCP: E1 BEARER CONTEXT MODIFICATION RESPONSE
CUCP->>CUCP: rrc_gNB_generate_dedicatedRRCReconfiguration
Note over CUCP: RRC_PDUSESSION_ESTABLISH
CUCP->>DU: rrc_deliver_dl_rrc_message
DU->>UE: RRCReconfiguration
UE->>DU: RRCReconfigurationComplete
DU->>CUCP: F1AP_UL_RRC_MESSAGE
Note over CUCP: rrc_gNB_decode_dcch
Note over CUCP: handle_rrcReconfigurationComplete
CUCP->>CUCP: rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP / rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP
loop pduSessions_to_addmod
Note over CUCP: Fill NGAP message (setup)
CUCP->>CUCP: add_pduSession (&UE->pduSessions)
end
loop UE->pduSessions_failed
Note over CUCP: Fill NGAP message (failed)
end
Note over CUCP: free UE->pduSessions_to_addmod
Note over CUCP: free UE->pduSessions_failed
CUCP->>Network: NGAP_PDUSESSION_SETUP_RESP
CUCP->>DU: ue_context_modification_request
```
# PDU Session Modification
```mermaid
sequenceDiagram
participant AMF
participant CUCP
participant DU
participant UE
AMF->>CUCP: PDUSessionResourceModifyRequest
Note over CUCP: ngap_gNB_handle_pdusession_modify_request
CUCP->>CUCP: decodePDUSessionResourceModify
CUCP->>CUCP: NGAP_PDUSESSION_MODIFY_REQ
CUCP->>CUCP: rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ
opt UE not found or AMF_UE_ID mismatch
CUCP->>AMF: NGAP_PDUSESSION_MODIFY_RESP (Failed PDU Session)
Note over CUCP: stop further processing
end
loop nb_pdusessions_tomodify
CUCP->>CUCP: find_pduSession(UE->pduSessions)
alt Session not found
CUCP->>CUCP: fill pdusessions_failed
else Session found
CUCP->>CUCP: cp_pdusession_resource_item_to_pdusession
Note over CUCP: copy to pdusession_t item (including QoS add/modify/release)
CUCP->>CUCP: update PDU Session in RRC (&UE->pduSessions)
Note over CUCP: Update QoS mapping in DRB
CUCP->>CUCP: add_pduSession(&UE->pduSessions_to_addmod)
end
end
alt seq_arr_size(UE->pduSessions_to_addmod)
CUCP->>CUCP: rrc_gNB_modify_dedicatedRRCReconfiguration
Note over CUCP: RRC_PDUSESSION_MODIFY
CUCP->>CUCP: Build DRB list and NAS list
CUCP->>DU: rrc_deliver_dl_rrc_message
DU->>UE: RRCReconfiguration (DCCH)
UE->>DU: RRCReconfigurationComplete
DU->>CUCP: F1AP_UL_RRC_MESSAGE
Note over CUCP: rrc_gNB_decode_dcch
Note over CUCP: handle_rrcReconfigurationComplete
CUCP->>CUCP: rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP
loop pduSessions_to_addmod
Note over CUCP: Fill NGAP message (modified)
end
loop UE->pduSessions_failed
Note over CUCP: Fill NGAP message (failed tp modify)
end
CUCP->>AMF: NGAP_PDUSESSION_MODIFY_RESP
Note over CUCP: free UE->pduSessions_to_addmod
Note over CUCP: free UE->pduSessions_failed
CUCP->>DU: ue_context_modification_request
else msg->nb_of_pdusessions_failed > 0
Note over CUCP: PDU Session failed to modify
CUCP->>AMF: NGAP_PDUSESSION_MODIFY_RESP
end
```
# PDU Session Release
```mermaid
sequenceDiagram
participant AMF
participant CUCP
participant CUUP
participant DU
participant UE
AMF->>CUCP: PDUSessionResourceReleaseCommand
Note over CUCP: ngap_gNB_handle_pdusession_release_command
CUCP->>CUCP: NGAP_PDUSESSION_RELEASE_COMMAND
CUCP->>CUCP: rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND
loop nb_pdusessions_torelease
CUCP->>CUCP: find_pduSession(UE->pduSessions_failed)
alt PDU Session is in the failed list
CUCP->>CUCP: add_pduSession_to_release()
else
CUCP->>CUCP: find_pduSession(UE->pduSessions)
alt PDU Session not found in setup list
CUCP->>CUCP: add_failed_pduSession()
else PDU is setup, add to addmod list
CUCP->>CUCP: add_pduSession_to_addmod()
CUCP->>CUCP: rm_pduSession()
Note over CUCP: Add to addmod and remove from setup list
Note over CUCP: Cleanup established DRBs
CUCP->>CUCP: fill E1AP message (numPDUSessionsRem++)
end
end
end
alt req.numPDUSessionsRem > 0
opt E1AP available
CUCP->>CUUP: e1_bearer_context_mod
CUUP->>CUUP: newGtpuDeleteOneTunnel
CUUP->>CUUP: nr_pdcp_release_drbs
CUUP->>CUUP: nr_sdap_delete_entity
end
Note over CUCP: RRC_PDUSESSION_RELEASE
CUCP->>CUCP: rrc_gNB_generate_dedicatedRRCReconfiguration_release
Note over CUCP: Build DRB_ReleaseList and NAS message (if present)
CUCP->>DU: rrc_deliver_dl_rrc_message
CUCP->>UE: RRCReconfiguration (DRB release + optional NAS)
UE->>DU: RRCReconfigurationComplete
DU->>CUCP: F1AP_UL_RRC_MESSAGE
Note over CUCP: handle_rrcReconfigurationComplete()
loop UE->pduSessions_to_addmod
Note over CUCP: move to pduSessions_to_release
Note over CUCP: cleanup pduSessions_to_addmod
end
CUCP->>CUCP: rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE
Note over CUCP: loop through pduSessions_to_release and pduSessions_failed
else
# CUCP->>CUCP: release_pduSessions
# Note over CUCP: GTP tunnel deletion
CUCP->>CUCP: rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE
Note over CUCP: loop through pduSessions_to_release and pduSessions_failed
end
```

View File

@@ -296,43 +296,6 @@ typedef struct drb_to_setup_s {
cell_group_id_t cellGroupList[E1AP_MAX_NUM_CELL_GROUPS];
} drb_to_setup_t;
typedef struct qos_characteristics_s {
union {
struct {
uint16_t fiveqi;
uint8_t qos_priority_level;
} non_dynamic;
struct {
// Range 5QI [0 - 255]
uint16_t fiveqi;
/* Range [0 - 15]
15 = "no priority," 1-14 = decreasing priority (1 highest), 0 = logical error if received */
uint8_t qos_priority_level;
// Range [0, 1023]: Upper bound for packet delay in 0.5ms units
uint16_t packet_delay_budget;
struct {
// PER = Scalar x 10^-k (k: 0-9)
uint8_t per_scalar;
uint8_t per_exponent;
} packet_error_rate;
} dynamic;
};
fiveQI_t qos_type;
} qos_characteristics_t;
typedef struct ngran_allocation_retention_priority_s {
uint16_t priority_level;
// Pre-emption capability on other QoS flows
uint8_t preemption_capability;
// Vulnerability of the QoS flow to pre-emption of other QoS flows
uint8_t preemption_vulnerability;
} ngran_allocation_retention_priority_t;
typedef struct qos_flow_level_qos_parameters_s {
qos_characteristics_t qos_characteristics;
ngran_allocation_retention_priority_t alloc_reten_priority; // additional members should be added!!
} qos_flow_level_qos_parameters_t;
// QoS Flow QoS Parameters List 9.3.1.25
typedef struct qos_flow_setup_e {
long qfi; // qos flow identifier
@@ -457,6 +420,16 @@ typedef struct pdu_session_to_mod_s {
DRB_nGRAN_to_mod_t DRBnGRanModList[E1AP_MAX_NUM_DRBS];
} pdu_session_to_mod_t;
/**
* PDU Session Resource To Remove List (clause 9.3.3.12)
*/
typedef struct {
// PDU Session ID (M)
long sessionId;
// Cause (O)
e1ap_cause_t cause;
} pdu_session_to_remove_t;
/**
* Bearer Context Setup Request message, clause 9.2.2.1 of 3GPP TS 38.463
*/
@@ -516,6 +489,9 @@ typedef struct e1ap_bearer_mod_req_s {
// NG-RAN PDU Session Resource To Modify List (O)
int numPDUSessionsMod;
pdu_session_to_mod_t pduSessionMod[E1AP_MAX_NUM_PDU_SESSIONS];
// NG-RAN PDU Session Resource To Remove List (O)
int numPDUSessionsRem;
pdu_session_to_remove_t pduSessionRem[E1AP_MAX_NUM_PDU_SESSIONS];
} e1ap_bearer_mod_req_t;
typedef struct e1ap_bearer_release_cmd_s {

View File

@@ -365,53 +365,13 @@ typedef struct f1ap_up_tnl_s {
uint16_t port;
} f1ap_up_tnl_t;
typedef enum preemption_capability_e {
SHALL_NOT_TRIGGER_PREEMPTION,
MAY_TRIGGER_PREEMPTION,
} preemption_capability_t;
typedef enum preemption_vulnerability_e {
NOT_PREEMPTABLE,
PREEMPTABLE,
} preemption_vulnerability_t;
typedef struct f1ap_qos_characteristics_s {
union {
struct {
long fiveqi;
long qos_priority_level;
} non_dynamic;
struct {
long fiveqi; // -1 -> optional
long qos_priority_level;
long packet_delay_budget;
struct {
long per_scalar;
long per_exponent;
} packet_error_rate;
} dynamic;
};
fiveQI_t qos_type;
} f1ap_qos_characteristics_t;
typedef struct f1ap_ngran_allocation_retention_priority_s {
uint16_t priority_level;
preemption_capability_t preemption_capability;
preemption_vulnerability_t preemption_vulnerability;
} f1ap_ngran_allocation_retention_priority_t;
typedef struct f1ap_qos_flow_level_qos_parameters_s {
f1ap_qos_characteristics_t qos_characteristics;
f1ap_ngran_allocation_retention_priority_t alloc_reten_priority;
} f1ap_qos_flow_level_qos_parameters_t;
typedef struct f1ap_flows_mapped_to_drb_s {
long qfi; // qos flow identifier
f1ap_qos_flow_level_qos_parameters_t qos_params;
qos_flow_level_qos_parameters_t qos_params;
} f1ap_flows_mapped_to_drb_t;
typedef struct f1ap_drb_information_s {
f1ap_qos_flow_level_qos_parameters_t drb_qos;
qos_flow_level_qos_parameters_t drb_qos;
f1ap_flows_mapped_to_drb_t *flows_mapped_to_drb;
uint8_t flows_to_be_setup_length;
} f1ap_drb_information_t;

View File

@@ -192,13 +192,13 @@ typedef enum ngap_rrc_establishment_cause_e {
NGAP_RRC_CAUSE_LAST
} ngap_rrc_establishment_cause_t;
typedef struct pdusession_level_qos_parameter_s {
/* QoS Flow Setup Request Item (9.3.4.1 3GPP TS 38.413) */
typedef struct {
// QoS Flow Identifier
uint8_t qfi;
uint64_t fiveQI;
uint64_t qos_priority;
fiveQI_t fiveQI_type;
ngap_allocation_retention_priority_t allocation_retention_priority;
} pdusession_level_qos_parameter_t;
// QoS Flow Level QoS Parameters (9.3.1.12 3GPP TS 38.413)
qos_flow_level_qos_parameters_t qos_params;
} qos_flow_setup_request_item_t;
typedef struct fiveg_s_tmsi_s {
uint16_t amf_set_id;
@@ -540,7 +540,7 @@ typedef struct ngap_downlink_nas_s {
/* PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */
typedef struct {
uint8_t nb_qos;
pdusession_level_qos_parameter_t qos[QOSFLOW_MAX_VALUE];
qos_flow_setup_request_item_t qos[QOSFLOW_MAX_VALUE];
pdu_session_type_t pdu_session_type;
// UPF endpoint of the NG-U (N3) transport bearer
gtpu_tunnel_t n3_incoming;
@@ -585,8 +585,7 @@ typedef struct ngap_initial_context_setup_req_s {
uint8_t mobility_restriction_flag;
ngap_mobility_restriction_t mobility_restriction;
/* Nas Pdu */
uint8_t nas_pdu_flag;
// NAS PDU: to be forwarded transparently towards the UE
byte_array_t nas_pdu;
} ngap_initial_context_setup_req_t;

View File

@@ -236,7 +236,7 @@ static E1AP_QoS_Flow_QoS_Parameter_Item_t e1_encode_qos_flow_to_setup(const qos_
dynamic_5QI->packetErrorRate.pER_Exponent = qos_char_in->dynamic.packet_error_rate.per_exponent;
}
// QoS Retention Priority
const ngran_allocation_retention_priority_t *rent_priority_in = &in->qos_params.alloc_reten_priority;
const ngran_allocation_retention_priority_t *rent_priority_in = &in->qos_params.arp;
E1AP_NGRANAllocationAndRetentionPriority_t *arp = &out.qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority;
arp->priorityLevel = rent_priority_in->priority_level;
arp->pre_emptionCapability = rent_priority_in->preemption_capability;
@@ -272,7 +272,7 @@ bool e1_decode_qos_flow_to_setup(qos_flow_to_setup_t *out, const E1AP_QoS_Flow_Q
break;
}
// NG-RAN Allocation and Retention Priority (M)
ngran_allocation_retention_priority_t *rent_priority = &out->qos_params.alloc_reten_priority;
ngran_allocation_retention_priority_t *rent_priority = &out->qos_params.arp;
const E1AP_NGRANAllocationAndRetentionPriority_t *aRP = &qosParams->nGRANallocationRetentionPriority;
rent_priority->priority_level = aRP->priorityLevel;
rent_priority->preemption_capability = aRP->pre_emptionCapability;
@@ -286,8 +286,8 @@ bool e1_decode_qos_flow_to_setup(qos_flow_to_setup_t *out, const E1AP_QoS_Flow_Q
static bool eq_qos_flow(const qos_flow_to_setup_t *a, const qos_flow_to_setup_t *b)
{
_E1_EQ_CHECK_LONG(a->qfi, b->qfi);
const ngran_allocation_retention_priority_t *arp_a = &a->qos_params.alloc_reten_priority;
const ngran_allocation_retention_priority_t *arp_b = &b->qos_params.alloc_reten_priority;
const ngran_allocation_retention_priority_t *arp_a = &a->qos_params.arp;
const ngran_allocation_retention_priority_t *arp_b = &b->qos_params.arp;
_E1_EQ_CHECK_INT(arp_a->preemption_capability, arp_b->preemption_capability);
_E1_EQ_CHECK_INT(arp_a->preemption_vulnerability, arp_b->preemption_vulnerability);
_E1_EQ_CHECK_INT(arp_a->priority_level, arp_b->priority_level);

View File

@@ -99,11 +99,11 @@ static void test_bearer_context_setup_request(void)
};
qos_flow_to_setup_t qos = {
.qfi = 1,
.qos_params.alloc_reten_priority.preemption_capability = E1AP_Pre_emptionCapability_shall_not_trigger_pre_emption,
.qos_params.alloc_reten_priority.preemption_vulnerability = E1AP_Pre_emptionVulnerability_not_pre_emptable,
.qos_params.alloc_reten_priority.priority_level = E1AP_PriorityLevel_highest,
.qos_params.qos_characteristics.non_dynamic.fiveqi = 9,
.qfi = 1,
.qos_params.arp.preemption_capability = E1AP_Pre_emptionCapability_shall_not_trigger_pre_emption,
.qos_params.arp.preemption_vulnerability = E1AP_Pre_emptionVulnerability_not_pre_emptable,
.qos_params.arp.priority_level = E1AP_PriorityLevel_highest,
.qos_params.qos_characteristics.non_dynamic.fiveqi = 9,
};
security_indication_t security = {
@@ -484,9 +484,9 @@ static void test_bearer_context_modification_request(void)
const qos_flow_to_setup_t dummy_qos_flows = {
.qfi = 9,
.qos_params.alloc_reten_priority.preemption_capability = E1AP_Pre_emptionCapability_may_trigger_pre_emption,
.qos_params.alloc_reten_priority.preemption_vulnerability = E1AP_Pre_emptionVulnerability_pre_emptable,
.qos_params.alloc_reten_priority.priority_level = E1AP_PriorityLevel_no_priority,
.qos_params.arp.preemption_capability = E1AP_Pre_emptionCapability_may_trigger_pre_emption,
.qos_params.arp.preemption_vulnerability = E1AP_Pre_emptionVulnerability_pre_emptable,
.qos_params.arp.priority_level = E1AP_PriorityLevel_no_priority,
.qos_params.qos_characteristics.non_dynamic.fiveqi = 9,
};

View File

@@ -27,10 +27,11 @@
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
#include "openair2/E2AP/flexric/src/util/time_now_us.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h"
#include "ds/seq_arr.h"
#if defined (NGRAN_GNB_CUCP)
#include "openair2/RRC/NR/rrc_gNB_UE_context.h"
#include "openair2/RRC/NR/rrc_gNB_radio_bearers.h"
#endif
bool read_gtp_sm(void * data)
@@ -62,15 +63,16 @@ bool read_gtp_sm(void * data)
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[0], ue_id_list[i]);
gtp->msg.ngut[i].rnti = ue_id_list[i];
int nb_pdu_session = ue_context_p->ue_context.nb_of_pdusessions;
int nb_pdu_session = seq_arr_size(ue_context_p->ue_context.pduSessions);
if (nb_pdu_session > 0) {
int nb_pdu_idx = nb_pdu_session - 1;
gtp->msg.ngut[i].teidgnb = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.n3_outgoing.teid;
gtp->msg.ngut[i].teidupf = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.n3_incoming.teid;
pdusession_t *session = find_pduSession(ue_context_p->ue_context.pduSessions, nb_pdu_idx);
gtp->msg.ngut[i].teidgnb = session->n3_outgoing.teid;
gtp->msg.ngut[i].teidupf = session->n3_incoming.teid;
// TODO: one PDU session has multiple QoS Flow
int nb_qos_flow = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.nb_qos;
int nb_qos_flow = session->nb_qos;
if (nb_qos_flow > 0) {
gtp->msg.ngut[i].qfi = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.qos[nb_qos_flow - 1].qfi;
gtp->msg.ngut[i].qfi = session->qos[nb_qos_flow - 1].qfi;
}
}
}

View File

@@ -27,6 +27,7 @@
#include "openair2/E1AP/e1ap_common.h"
#include "openair2/E2AP/flexric/src/util/time_now_us.h"
#include "openair2/F1AP/f1ap_ids.h"
#include "ds/seq_arr.h"
static pthread_once_t once_kpm_mutex = PTHREAD_ONCE_INIT;
@@ -209,8 +210,7 @@ static arr_ue_id_t filter_ues_by_s_nssai_in_cu(const test_info_lst_t test_info)
struct rrc_gNB_ue_context_s* rrc_ue_context = NULL;
RB_FOREACH(rrc_ue_context, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) {
gNB_RRC_UE_t *ue = &rrc_ue_context->ue_context;
for (int p = 0; p < ue->nb_of_pdusessions; ++p) {
pdusession_t *pdu = &ue->pduSession[p].param;
FOR_EACH_SEQ_ARR(pdusession_t *, pdu, ue->pduSessions) {
if (nssai_matches(pdu->nssai, sst, sd)) {
arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB_CU](ue, 0, 0);

View File

@@ -44,11 +44,11 @@
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
#endif
static void f1ap_write_drb_qos_param(const f1ap_qos_flow_level_qos_parameters_t *drb_qos_in, F1AP_QoSFlowLevelQoSParameters_t *asn1_qosparam)
static void f1ap_write_drb_qos_param(const qos_flow_level_qos_parameters_t *drb_qos_in, F1AP_QoSFlowLevelQoSParameters_t *asn1_qosparam)
{
int type = drb_qos_in->qos_characteristics.qos_type;
const f1ap_qos_characteristics_t *drb_qos_char_in = &drb_qos_in->qos_characteristics;
const qos_characteristics_t *drb_qos_char_in = &drb_qos_in->qos_characteristics;
if (type == NON_DYNAMIC) {
asn1_qosparam->qoS_Characteristics.present = F1AP_QoS_Characteristics_PR_non_Dynamic_5QI;
asn1cCalloc(asn1_qosparam->qoS_Characteristics.choice.non_Dynamic_5QI, tmp);
@@ -77,10 +77,9 @@ static void f1ap_write_drb_qos_param(const f1ap_qos_flow_level_qos_parameters_t
}
{
asn1_qosparam->nGRANallocationRetentionPriority.priorityLevel = drb_qos_in->alloc_reten_priority.priority_level;
asn1_qosparam->nGRANallocationRetentionPriority.pre_emptionCapability = drb_qos_in->alloc_reten_priority.preemption_capability;
asn1_qosparam->nGRANallocationRetentionPriority.pre_emptionVulnerability =
drb_qos_in->alloc_reten_priority.preemption_vulnerability;
asn1_qosparam->nGRANallocationRetentionPriority.priorityLevel = drb_qos_in->arp.priority_level;
asn1_qosparam->nGRANallocationRetentionPriority.pre_emptionCapability = drb_qos_in->arp.preemption_capability;
asn1_qosparam->nGRANallocationRetentionPriority.pre_emptionVulnerability = drb_qos_in->arp.preemption_vulnerability;
} // nGRANallocationRetentionPriority
/* OPTIONAL */
@@ -132,11 +131,11 @@ static void f1ap_write_flows_mapped(const f1ap_flows_mapped_to_drb_t *flows_mapp
flow_item->qoSFlowIdentifier = qos_flow_in->qfi;
/* qoSFlowLevelQoSParameters */
const f1ap_qos_flow_level_qos_parameters_t *flow_qos_params_in = &qos_flow_in->qos_params;
const qos_flow_level_qos_parameters_t *flow_qos_params_in = &qos_flow_in->qos_params;
/* qoS_Characteristics */
F1AP_QoS_Characteristics_t *QosParams = &flow_item->qoSFlowLevelQoSParameters.qoS_Characteristics;
const f1ap_qos_characteristics_t *flow_qos_char_in = &flow_qos_params_in->qos_characteristics;
const qos_characteristics_t *flow_qos_char_in = &flow_qos_params_in->qos_characteristics;
int type = flow_qos_params_in->qos_characteristics.qos_type;
if (type == NON_DYNAMIC) {
@@ -168,12 +167,10 @@ static void f1ap_write_flows_mapped(const f1ap_flows_mapped_to_drb_t *flows_mapp
/* nGRANallocationRetentionPriority */
{
flow_item->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority.priorityLevel =
flow_qos_params_in->alloc_reten_priority.priority_level;
flow_item->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority.pre_emptionCapability =
flow_qos_params_in->alloc_reten_priority.preemption_capability;
flow_item->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority.pre_emptionVulnerability =
flow_qos_params_in->alloc_reten_priority.preemption_vulnerability;
F1AP_NGRANAllocationAndRetentionPriority_t *arp = &flow_item->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority;
arp->priorityLevel = flow_qos_params_in->arp.priority_level;
arp->pre_emptionCapability = flow_qos_params_in->arp.preemption_capability;
arp->pre_emptionVulnerability = flow_qos_params_in->arp.preemption_vulnerability;
} // nGRANallocationRetentionPriority
/* OPTIONAL */

View File

@@ -39,9 +39,9 @@
#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h"
static void f1ap_read_drb_qos_param(const F1AP_QoSFlowLevelQoSParameters_t *asn1_qos, f1ap_qos_flow_level_qos_parameters_t *drb_qos)
static void f1ap_read_drb_qos_param(const F1AP_QoSFlowLevelQoSParameters_t *asn1_qos, qos_flow_level_qos_parameters_t *drb_qos)
{
f1ap_qos_characteristics_t *drb_qos_char = &drb_qos->qos_characteristics;
qos_characteristics_t *drb_qos_char = &drb_qos->qos_characteristics;
const F1AP_QoS_Characteristics_t *dRB_QoS_Char = &asn1_qos->qoS_Characteristics;
if (dRB_QoS_Char->present == F1AP_QoS_Characteristics_PR_non_Dynamic_5QI) {
@@ -61,9 +61,9 @@ static void f1ap_read_drb_qos_param(const F1AP_QoSFlowLevelQoSParameters_t *asn1
}
/* nGRANallocationRetentionPriority */
drb_qos->alloc_reten_priority.priority_level = asn1_qos->nGRANallocationRetentionPriority.priorityLevel;
drb_qos->alloc_reten_priority.preemption_vulnerability = asn1_qos->nGRANallocationRetentionPriority.pre_emptionVulnerability;
drb_qos->alloc_reten_priority.preemption_capability = asn1_qos->nGRANallocationRetentionPriority.pre_emptionVulnerability;
drb_qos->arp.priority_level = asn1_qos->nGRANallocationRetentionPriority.priorityLevel;
drb_qos->arp.preemption_vulnerability = asn1_qos->nGRANallocationRetentionPriority.pre_emptionVulnerability;
drb_qos->arp.preemption_capability = asn1_qos->nGRANallocationRetentionPriority.pre_emptionCapability;
}
static void f1ap_read_flows_mapped(const F1AP_Flows_Mapped_To_DRB_List_t *asn1_flows_mapped, f1ap_flows_mapped_to_drb_t *flows_mapped, int n)
@@ -76,11 +76,11 @@ static void f1ap_read_flows_mapped(const F1AP_Flows_Mapped_To_DRB_List_t *asn1_f
/* QoS-Flow-Level-QoS-Parameters */
{
f1ap_qos_flow_level_qos_parameters_t *flow_qos = &flows_mapped_to_drb->qos_params;
qos_flow_level_qos_parameters_t *flow_qos = &flows_mapped_to_drb->qos_params;
const F1AP_QoSFlowLevelQoSParameters_t *Flow_QoS = &flows_Mapped_To_Drb->qoSFlowLevelQoSParameters;
/* QoS Characteristics*/
f1ap_qos_characteristics_t *flow_qos_char = &flow_qos->qos_characteristics;
qos_characteristics_t *flow_qos_char = &flow_qos->qos_characteristics;
const F1AP_QoS_Characteristics_t *Flow_QoS_Char = &Flow_QoS->qoS_Characteristics;
if (Flow_QoS_Char->present == F1AP_QoS_Characteristics_PR_non_Dynamic_5QI) {
@@ -100,9 +100,9 @@ static void f1ap_read_flows_mapped(const F1AP_Flows_Mapped_To_DRB_List_t *asn1_f
}
/* nGRANallocationRetentionPriority */
flow_qos->alloc_reten_priority.priority_level = Flow_QoS->nGRANallocationRetentionPriority.priorityLevel;
flow_qos->alloc_reten_priority.preemption_vulnerability = Flow_QoS->nGRANallocationRetentionPriority.pre_emptionVulnerability;
flow_qos->alloc_reten_priority.preemption_capability = Flow_QoS->nGRANallocationRetentionPriority.pre_emptionVulnerability;
flow_qos->arp.priority_level = Flow_QoS->nGRANallocationRetentionPriority.priorityLevel;
flow_qos->arp.preemption_vulnerability = Flow_QoS->nGRANallocationRetentionPriority.pre_emptionVulnerability;
flow_qos->arp.preemption_capability = Flow_QoS->nGRANallocationRetentionPriority.pre_emptionVulnerability;
}
}
}

View File

@@ -284,7 +284,7 @@ static int get_non_dynamic_priority(int fiveqi)
return 0;
}
static NR_QoS_config_t get_qos_config(const f1ap_qos_characteristics_t *qos_char)
static NR_QoS_config_t get_qos_config(const qos_characteristics_t *qos_char)
{
NR_QoS_config_t qos_c = {0};
switch (qos_char->qos_type) {
@@ -369,8 +369,6 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE,
NR_CellGroupConfig_t *cellGroupConfig)
{
DevAssert(req_drbs != NULL && cellGroupConfig != NULL);
instance_t f1inst = get_f1_gtp_instance();
cellGroupConfig->rlc_BearerToReleaseList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToReleaseList));
AssertFatal(cellGroupConfig->rlc_BearerToReleaseList != NULL, "out of memory\n");
@@ -390,8 +388,6 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE,
if (idx < cellGroupConfig->rlc_BearerToAddModList->list.count) {
nr_mac_remove_lcid(&UE->UE_sched_ctrl, lcid);
nr_rlc_release_entity(UE->rnti, lcid);
if (f1inst >= 0)
newGtpuDeleteOneTunnel(f1inst, UE->rnti, drb->rb_id);
asn_sequence_del(&cellGroupConfig->rlc_BearerToAddModList->list, idx, 1);
long *plcid = malloc(sizeof(*plcid));
AssertFatal(plcid, "out of memory\n");

View File

@@ -247,7 +247,7 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req)
*/
void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
{
AssertFatal(req->numPDUSessionsMod > 0, "need at least one PDU session to modify\n");
AssertFatal(req->numPDUSessionsMod > 0 || req->numPDUSessionsRem > 0, "need at least one PDU session to modify\n");
e1ap_bearer_modif_resp_t modif = {
.gNB_cu_cp_ue_id = req->gNB_cu_cp_ue_id,
@@ -303,6 +303,17 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
}
}
/* PDU Session Resource To Remove List (see 9.3.3.12 of TS 38.463) */
for (int i = 0; i < req->numPDUSessionsRem; i++) {
instance_t f1inst = get_f1_gtp_instance();
instance_t n3inst = get_n3_gtp_instance();
instance_t inst = f1inst >= 0 ? f1inst : n3inst; // is there F1-U?
// NOTE (TODO): shouldn't both F1 and N3 tunnels be removed ?
newGtpuDeleteOneTunnel(inst, req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId);
nr_pdcp_release_drbs(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId);
nr_sdap_delete_entity(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId);
}
get_e1_if()->bearer_modif_response(&modif);
}

View File

@@ -724,6 +724,19 @@ void nr_pdcp_add_drbs(eNB_flag_t enb_flag,
LOG_W(PDCP, "nr_pdcp_add_drbs() with void list\n");
}
/** @brief Release all DRBs for @param pdusession_id */
void nr_pdcp_release_drbs(ue_id_t ue_id, long pdusession_id)
{
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id);
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
const nr_pdcp_entity_t *drb = ue->drb[i];
if (drb && drb->pdusession_id == pdusession_id)
nr_pdcp_release_drb(ue_id, i + 1);
}
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
}
uint64_t get_pdcp_optmask(void)
{
return pdcp_optmask;

View File

@@ -77,6 +77,7 @@ void nr_pdcp_reconfigure_srb(ue_id_t ue_id, int srb_id, long t_Reordering);
void nr_pdcp_reconfigure_drb(ue_id_t ue_id, int drb_id, NR_PDCP_Config_t *pdcp_config);
void nr_pdcp_release_srb(ue_id_t ue_id, int srb_id);
void nr_pdcp_release_drb(ue_id_t ue_id, int drb_id);
void nr_pdcp_release_drbs(ue_id_t ue_id, long pdusession_id);
void add_srb(int is_gnb,
ue_id_t UEid,

View File

@@ -1 +1,5 @@
add_subdirectory(MESSAGES)
if(ENABLE_TESTS)
add_subdirectory(tests)
endif()

View File

@@ -47,6 +47,7 @@
#include "asn1_msg.h"
#include "../nr_rrc_proto.h"
#include "LAYER2/nr_pdcp/nr_pdcp_asn1_utils.h"
#include "LAYER2/nr_pdcp/nr_pdcp_configuration.h"
#include "openair3/SECU/key_nas_deriver.h"
@@ -271,6 +272,72 @@ int do_RRCReject(uint8_t *const buffer)
return (enc_rval.encoded + 7) / 8;
}
static NR_PDCP_Config_t *get_default_PDCP_config(const bool drb_integrity, const bool drb_ciphering, const nr_pdcp_configuration_t *pdcp)
{
NR_PDCP_Config_t *out = calloc_or_fail(1, sizeof(*out));
asn1cCallocOne(out->t_Reordering, encode_t_reordering(pdcp->drb.t_reordering));
if (drb_ciphering) {
asn1cCalloc(out->ext1, ext1);
asn1cCallocOne(ext1->cipheringDisabled, NR_PDCP_Config__ext1__cipheringDisabled_true);
}
asn1cCalloc(out->drb, drb);
asn1cCallocOne(drb->discardTimer, encode_discard_timer(pdcp->drb.discard_timer));
asn1cCallocOne(drb->pdcp_SN_SizeUL, encode_sn_size_ul(pdcp->drb.sn_size));
asn1cCallocOne(drb->pdcp_SN_SizeDL, encode_sn_size_dl(pdcp->drb.sn_size));
drb->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed;
drb->headerCompression.choice.notUsed = 0;
if (drb_integrity) {
asn1cCallocOne(drb->integrityProtection, NR_PDCP_Config__drb__integrityProtection_enabled);
}
return out;
}
NR_DRB_ToAddMod_t *get_DRB_ToAddMod(const int rb_id,
const bool do_drb_integrity,
const bool do_drb_ciphering,
const bool reestablish,
const nr_sdap_configuration_t *sdap_config,
const int *eps_bearer_id,
const nr_pdcp_configuration_t *pdcp_config)
{
NR_DRB_ToAddMod_t *drb_ToAddMod = calloc_or_fail(1, sizeof(*drb_ToAddMod));
drb_ToAddMod->drb_Identity = rb_id;
if (reestablish) {
asn1cCallocOne(drb_ToAddMod->reestablishPDCP, NR_DRB_ToAddMod__reestablishPDCP_true);
}
// cn-association
asn1cCalloc(drb_ToAddMod->cnAssociation, cn_association);
if (sdap_config) {
// SDAP
cn_association->present = NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config;
asn1cCalloc(cn_association->choice.sdap_Config, sc);
sc->defaultDRB = true;
sc->pdu_Session = sdap_config->pdu_Session_id;
sc->sdap_HeaderDL = sdap_config->sdap_HeaderDL;
sc->sdap_HeaderUL = sdap_config->sdap_HeaderUL;
// QoS
asn1cCalloc(sc->mappedQoS_FlowsToAdd, mappedQoS_FlowsToAdd);
for (int q = 0; q < sdap_config->nb_qos; q++) {
NR_QFI_t *qfi = calloc_or_fail(1, sizeof(*qfi));
*qfi = sdap_config->mappedQoS_FlowsToAdd[q];
LOG_D(NR_RRC, "Adding QFI %ld to DRB %d\n", *qfi, rb_id);
asn1cSeqAdd(&mappedQoS_FlowsToAdd->list, qfi);
}
} else if (eps_bearer_id) {
// EPS
cn_association->present = NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity;
cn_association->choice.eps_BearerIdentity = *eps_bearer_id;
} else {
LOG_E(RRC, "Failed to configure cnAssociation: neither SDAP nor EPS bearer ID provided\n");
ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, drb_ToAddMod);
return NULL;
}
// PDCP
drb_ToAddMod->pdcp_Config = get_default_PDCP_config(do_drb_integrity, do_drb_ciphering, pdcp_config);
xer_fprint(stdout, &asn_DEF_NR_DRB_ToAddMod, drb_ToAddMod);
return drb_ToAddMod;
}
/* returns a default radio bearer config suitable for NSA etc */
NR_RadioBearerConfig_t *get_default_rbconfig(int eps_bearer_id,
int rb_id,
@@ -278,36 +345,10 @@ NR_RadioBearerConfig_t *get_default_rbconfig(int eps_bearer_id,
e_NR_SecurityConfig__keyToUse key_to_use,
const nr_pdcp_configuration_t *pdcp_config)
{
NR_RadioBearerConfig_t *rbconfig = calloc(1, sizeof(*rbconfig));
rbconfig->srb_ToAddModList = NULL;
rbconfig->srb3_ToRelease = NULL;
rbconfig->drb_ToAddModList = calloc(1,sizeof(*rbconfig->drb_ToAddModList));
NR_DRB_ToAddMod_t *drb_ToAddMod = calloc(1,sizeof(*drb_ToAddMod));
drb_ToAddMod->cnAssociation = calloc(1,sizeof(*drb_ToAddMod->cnAssociation));
drb_ToAddMod->cnAssociation->present = NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity;
drb_ToAddMod->cnAssociation->choice.eps_BearerIdentity= eps_bearer_id;
drb_ToAddMod->drb_Identity = rb_id;
drb_ToAddMod->reestablishPDCP = NULL;
drb_ToAddMod->recoverPDCP = NULL;
drb_ToAddMod->pdcp_Config = calloc(1,sizeof(*drb_ToAddMod->pdcp_Config));
asn1cCalloc(drb_ToAddMod->pdcp_Config->drb, drb);
asn1cCallocOne(drb->discardTimer, encode_discard_timer(pdcp_config->drb.discard_timer));
asn1cCallocOne(drb->pdcp_SN_SizeUL, encode_sn_size_ul(pdcp_config->drb.sn_size));
asn1cCallocOne(drb->pdcp_SN_SizeDL, encode_sn_size_dl(pdcp_config->drb.sn_size));
drb->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed;
drb->headerCompression.choice.notUsed = 0;
drb->integrityProtection = NULL;
drb->statusReportRequired = NULL;
drb->outOfOrderDelivery = NULL;
drb_ToAddMod->pdcp_Config->moreThanOneRLC = NULL;
asn1cCallocOne(drb_ToAddMod->pdcp_Config->t_Reordering, encode_t_reordering(pdcp_config->drb.t_reordering));
drb_ToAddMod->pdcp_Config->ext1 = NULL;
asn1cSeqAdd(&rbconfig->drb_ToAddModList->list,drb_ToAddMod);
rbconfig->drb_ToReleaseList = NULL;
NR_RadioBearerConfig_t *rbconfig = calloc_or_fail(1, sizeof(*rbconfig));
rbconfig->drb_ToAddModList = calloc_or_fail(1, sizeof(*rbconfig->drb_ToAddModList));
NR_DRB_ToAddMod_t *drb_ToAddMod = get_DRB_ToAddMod(rb_id, false, false, false, NULL, &eps_bearer_id, pdcp_config);
asn1cSeqAdd(&rbconfig->drb_ToAddModList->list, drb_ToAddMod);
asn1cCalloc(rbconfig->securityConfig, secConf);
asn1cCalloc(secConf->securityAlgorithmConfig, secConfAlgo);
secConfAlgo->cipheringAlgorithm = ciphering_algorithm;

View File

@@ -50,6 +50,8 @@
#include "NR_ReestablishmentCause.h"
#include "NR_SRB-ToAddModList.h"
#include "NR_SecurityConfig.h"
#include "NR_SDAP-Config.h"
#include "NR_asn_constant.h"
#include "ds/seq_arr.h"
#include "ds/byte_array.h"
#include "rrc_messages_types.h"
@@ -70,6 +72,14 @@ typedef struct {
int nextHopChainingCount;
} nr_rrc_reconfig_param_t;
typedef struct {
int pdu_Session_id;
e_NR_SDAP_Config__sdap_HeaderDL sdap_HeaderDL;
e_NR_SDAP_Config__sdap_HeaderUL sdap_HeaderUL;
int mappedQoS_FlowsToAdd[NR_maxNrofQFIs];
int nb_qos;
} nr_sdap_configuration_t;
/*
* The variant of the above function which dumps the BASIC-XER (XER_F_BASIC)
* output into the chosen string buffer.
@@ -155,4 +165,12 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
void free_MeasConfig(NR_MeasConfig_t *mc);
int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi);
NR_DRB_ToAddMod_t *get_DRB_ToAddMod(const int rb_id,
const bool do_drb_integrity,
const bool do_drb_ciphering,
const bool reestablish,
const nr_sdap_configuration_t *sdap_config,
const int *eps_bearer_id,
const nr_pdcp_configuration_t *pdcp_config);
#endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */

View File

@@ -88,24 +88,19 @@ typedef struct nr_e_rab_param_s {
uint8_t xid; // transaction_id
} __attribute__ ((__packed__)) nr_e_rab_param_t;
typedef enum pdu_session_satus_e {
PDU_SESSION_STATUS_NEW,
PDU_SESSION_STATUS_DONE,
PDU_SESSION_STATUS_ESTABLISHED,
PDU_SESSION_STATUS_REESTABLISHED, // after HO
PDU_SESSION_STATUS_TOMODIFY, // ENDC NSA
PDU_SESSION_STATUS_FAILED,
PDU_SESSION_STATUS_TORELEASE, // to release DRB between eNB and UE
PDU_SESSION_STATUS_RELEASED
} pdu_session_status_t;
typedef struct {
uint8_t qfi;
qos_flow_level_qos_parameters_t qos_params;
} pdusession_qos_t;
typedef struct pdusession_s {
// RRC transaction ID
uint8_t xid;
/* Unique pdusession_id for the UE. */
int pdusession_id;
byte_array_t nas_pdu;
uint8_t nb_qos;
/* Quality of service for this pdusession */
pdusession_level_qos_parameter_t qos[QOSFLOW_MAX_VALUE];
// QoS Flows mapped to current PDU Session
pdusession_qos_t qos[QOSFLOW_MAX_VALUE];
/* The transport layer address for the IP packets */
pdu_session_type_t pdu_session_type;
// NG-RAN endpoint of the NG-U (N3) transport bearer
@@ -115,41 +110,21 @@ typedef struct pdusession_s {
nssai_t nssai;
} pdusession_t;
typedef struct pdu_session_param_s {
pdusession_t param;
pdu_session_status_t status;
uint8_t xid; // transaction_id
typedef struct {
int pdusession_id;
ngap_cause_t cause;
} rrc_pdu_session_param_t;
uint8_t xid;
} rrc_pdusession_failed_t;
typedef struct {
int pdusession_id;
uint8_t xid;
} rrc_pdusession_release_t;
typedef struct drb_s {
int status;
int drb_id;
struct cnAssociation_s {
int present;
int eps_BearerIdentity;
struct sdap_config_s {
bool defaultDRB;
int pdusession_id;
int sdap_HeaderDL;
int sdap_HeaderUL;
int mappedQoS_FlowsToAdd[QOSFLOW_MAX_VALUE];
} sdap_config;
} cnAssociation;
struct pdcp_config_s {
int discardTimer;
int pdcp_SN_SizeUL;
int pdcp_SN_SizeDL;
int t_Reordering;
int integrityProtection;
struct headerCompression_s {
int NotUsed;
int present;
} headerCompression;
struct ext1_s {
int cipheringDisabled;
} ext1;
} pdcp_config;
int pdusession_id;
// F1-U Downlink Tunnel Config (on DU side)
gtpu_tunnel_t du_tunnel_config;
// F1-U Uplink Tunnel Config (on CU-UP side)
@@ -175,7 +150,6 @@ typedef struct nr_handover_context_s nr_handover_context_t;
typedef struct gNB_RRC_UE_s {
time_t last_seen; // last time this UE has been accessed
drb_t established_drbs[MAX_DRBS_PER_UE];
NR_DRB_ToReleaseList_t *DRB_ReleaseList;
NR_SRB_INFO_TABLE_ENTRY Srb[NR_NUM_SRB];
@@ -231,8 +205,13 @@ typedef struct gNB_RRC_UE_s {
rb_id_t nsa_gtp_psi[S1AP_MAX_E_RAB];
//SA block
int nb_of_pdusessions;
rrc_pdu_session_param_t pduSession[NGAP_MAX_PDU_SESSION];
seq_arr_t *pduSessions;
seq_arr_t *pduSessions_to_addmod;
seq_arr_t *pduSessions_failed;
seq_arr_t *pduSessions_to_release;
// Established DRBs
seq_arr_t *drbs;
rrc_action_t xids[NR_RRC_TRANSACTION_IDENTIFIER_NUMBER];
uint8_t e_rab_release_command_flag;
uint32_t ue_rrc_inactivity_timer;
@@ -247,7 +226,7 @@ typedef struct gNB_RRC_UE_s {
pdusession_t *initial_pdus;
/* Nas Pdu */
byte_array_t nas_pdu;
seq_arr_t *nas_pdu;
/* hack, see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() for more info */
int max_delays_pdu_session;
@@ -395,6 +374,12 @@ typedef struct gNB_RRC_INST_s {
nr_rlc_configuration_t rlc_config;
} gNB_RRC_INST;
typedef struct {
gNB_RRC_INST *rrc;
f1ap_ue_context_modif_req_t *modification_req;
sctp_assoc_t assoc_id;
} deliver_ue_ctxt_modification_data_t;
#define UE_LOG_FMT "(cellID %lx, UE ID %d RNTI %04x)"
#define UE_LOG_ARGS(ue_context) (ue_context)->nr_cellid, (ue_context)->rrc_ue_id, (ue_context)->rnti

View File

@@ -54,7 +54,7 @@ int parse_CG_ConfigInfo(gNB_RRC_INST *rrc, NR_CG_ConfigInfo_t *CG_ConfigInfo, x2
void rrc_gNB_generate_SecurityModeCommand(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p);
void rrc_forward_ue_nas_message(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE);
void rrc_forward_ue_nas_message(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, byte_array_t *pdu);
unsigned int rrc_gNB_get_next_transaction_identifier(module_id_t gnb_mod_idP);
@@ -122,7 +122,10 @@ byte_array_t rrc_gNB_encode_RRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t *
nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t srb_reest_bitmap, bool drb_reestablish);
pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession);
f1ap_qos_characteristics_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param);
qos_characteristics_t get_qos_characteristics(const int qfi, pdusession_t *pduSession);
void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration);
void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id);
#endif

View File

@@ -97,6 +97,8 @@
#include "NR_DL-DCCH-Message.h"
#include "ds/byte_array.h"
#include "alg/find.h"
#include "5g_platform_types.h"
#include "E1AP_RLC-Mode.h"
#ifdef E2_AGENT
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
@@ -299,19 +301,33 @@ static NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablis
return list;
}
static nr_sdap_configuration_t get_sdap_config(pdusession_t *pduSession, int pdusession_id, const bool enable_sdap)
{
nr_sdap_configuration_t sdap = {.pdu_Session_id = pdusession_id, .nb_qos = pduSession->nb_qos};
for (int q = 0; q < sdap.nb_qos; q++) {
sdap.mappedQoS_FlowsToAdd[q] = pduSession->qos[q].qfi;
}
sdap.sdap_HeaderUL = enable_sdap ? 0 : NR_SDAP_Config__sdap_HeaderUL_absent;
sdap.sdap_HeaderDL = enable_sdap ? 0 : NR_SDAP_Config__sdap_HeaderDL_absent;
return sdap;
}
static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
{
NR_DRB_ToAddMod_t *DRB_config = NULL;
gNB_RRC_INST *rrc = RC.nrrrc[0];
NR_DRB_ToAddModList_t *DRB_configList = CALLOC(sizeof(*DRB_configList), 1);
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
if (ue->established_drbs[i].status != DRB_INACTIVE) {
DRB_config = generateDRB_ASN1(&ue->established_drbs[i]);
if (reestablish) {
asn1cCallocOne(DRB_config->reestablishPDCP, NR_DRB_ToAddMod__reestablishPDCP_true);
}
asn1cSeqAdd(&DRB_configList->list, DRB_config);
FOR_EACH_SEQ_ARR(drb_t *, drb, ue->drbs) {
bool do_integrity = rrc->security.do_drb_integrity;
bool do_ciphering = rrc->security.do_drb_ciphering;
pdusession_t *pduSession = (pdusession_t *)find_pduSession(ue->pduSessions_to_addmod, drb->pdusession_id);
if (!pduSession) {
LOG_D(NR_RRC, "PDU Session %d not found in UE->pduSessions_to_addmod, skip in DRB list\n", drb->pdusession_id);
continue;
}
nr_sdap_configuration_t sdap = get_sdap_config(pduSession, drb->pdusession_id, rrc->configuration.enable_sdap);
NR_DRB_ToAddMod_t *DRB_ToAddMod = get_DRB_ToAddMod(drb->drb_id, do_integrity, do_ciphering, reestablish, &sdap, NULL, &rrc->pdcp_config);
asn1cSeqAdd(&DRB_configList->list, DRB_ToAddMod);
}
if (DRB_configList->list.count == 0) {
free(DRB_configList);
@@ -630,20 +646,18 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC
.srb_config_list = SRBs};
UE->DRB_ReleaseList = NULL; // pointer transferred to params
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
if (UE->pduSession[i].param.nas_pdu.len > 0) {
params.dedicated_NAS_msg_list[params.num_nas_msg++] = UE->pduSession[i].param.nas_pdu;
UE->pduSession[i].param.nas_pdu.buf = NULL;
UE->pduSession[i].param.nas_pdu.len = 0;
LOG_D(NR_RRC, "Transfer NAS info with size %ld (pdusession idx %d) to RRCReconfiguration params\n", UE->pduSession[i].param.nas_pdu.len, i);
// Loop through stored NAS PDUs
FOR_EACH_SEQ_ARR(byte_array_t *, pdu, UE->nas_pdu) {
if (pdu->len > 0) {
params.dedicated_NAS_msg_list[params.num_nas_msg++] = *pdu;
LOG_D(NR_RRC, "Transfer NAS info with size %ld to RRCReconfiguration params\n", pdu->len);
pdu->buf = NULL;
pdu->len = 0;
}
}
if (UE->nas_pdu.len > 0) {
params.dedicated_NAS_msg_list[params.num_nas_msg++] = UE->nas_pdu;
UE->nas_pdu.buf = NULL;
UE->nas_pdu.len = 0;
LOG_D(NR_RRC, "Transfer NAS info with size %ld to RRCReconfiguration params\n", UE->nas_pdu.len);
}
// Free seq_arr
free(UE->nas_pdu);
UE->nas_pdu = NULL;
return params;
}
@@ -663,20 +677,18 @@ static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_
{
/* do not re-establish PDCP for any bearer */
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue_p, 0, false);
ue_p->xids[params.transaction_id] = RRC_PDUSESSION_ESTABLISH;
if (params.num_nas_msg)
ue_p->xids[params.transaction_id] = RRC_PDUSESSION_ESTABLISH;
else
ue_p->xids[params.transaction_id] = RRC_DEDICATED_RECONF;
byte_array_t msg = rrc_gNB_encode_RRCReconfiguration(rrc, ue_p, params);
if (msg.len <= 0) {
LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id);
return;
}
for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
if (ue_p->pduSession[i].param.nas_pdu.buf != NULL) {
ue_p->pduSession[i].xid = params.transaction_id;
}
if (ue_p->pduSession[i].status < PDU_SESSION_STATUS_ESTABLISHED) {
ue_p->pduSession[i].status = PDU_SESSION_STATUS_DONE;
}
FOR_EACH_SEQ_ARR(pdusession_t *, session, ue_p->pduSessions_to_addmod) {
session->xid = params.transaction_id; // Update transaction ID
}
LOG_UE_DL_EVENT(ue_p, "Generate RRCReconfiguration (bytes %ld, xid %d)\n", msg.len, params.transaction_id);
@@ -692,40 +704,13 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue_p, 0, false);
ue_p->xids[params.transaction_id] = RRC_PDUSESSION_MODIFY;
for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
rrc_pdu_session_param_t *session = &ue_p->pduSession[i];
// bypass the new and already configured pdu sessions
if (session->status >= PDU_SESSION_STATUS_DONE) {
session->xid = params.transaction_id;
continue;
}
if (session->cause.type != NGAP_CAUSE_NOTHING) {
// set xid of failure pdu session
session->xid = params.transaction_id;
session->status = PDU_SESSION_STATUS_FAILED;
continue;
}
// search exist DRB_config
int j;
for (j = 0; i < MAX_DRBS_PER_UE; j++) {
if (ue_p->established_drbs[j].status != DRB_INACTIVE
&& ue_p->established_drbs[j].cnAssociation.sdap_config.pdusession_id == session->param.pdusession_id)
break;
}
if (j == MAX_DRBS_PER_UE) {
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CauseRadioNetwork_unspecified};
session->xid = params.transaction_id;
session->status = PDU_SESSION_STATUS_FAILED;
session->cause = cause;
continue;
}
FOR_EACH_SEQ_ARR(pdusession_t *, session, ue_p->pduSessions_to_addmod) {
session->xid = params.transaction_id; // Update transaction ID
// Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping
for (qos_flow_index = 0; qos_flow_index < session->param.nb_qos; qos_flow_index++) {
switch (session->param.qos[qos_flow_index].fiveQI) {
for (qos_flow_index = 0; qos_flow_index < session->nb_qos; qos_flow_index++) {
qos_characteristics_t *qos = &session->qos[qos_flow_index].qos_params.qos_characteristics;
int fiveQI = qos->qos_type == NON_DYNAMIC ? qos->non_dynamic.fiveqi : qos->dynamic.fiveqi;
switch (fiveQI) {
case 1: //100ms
case 2: //150ms
case 3: //50ms
@@ -739,18 +724,14 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t
break;
default:
LOG_E(NR_RRC, "not supported 5qi %lu\n", session->param.qos[qos_flow_index].fiveQI);
LOG_E(NR_RRC, "not supported 5qi %d\n", fiveQI);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CauseRadioNetwork_not_supported_5QI_value};
session->status = PDU_SESSION_STATUS_FAILED;
session->xid = params.transaction_id;
session->cause = cause;
rrc_pdusession_failed_t fail = {.cause = cause, .pdusession_id = session->pdusession_id, .xid = params.transaction_id};
add_failed_pduSession(&ue_p->pduSessions_failed, ue_p->rrc_ue_id, fail);
continue;
}
LOG_I(NR_RRC, "index %d, QOS flow %d, 5QI %ld \n", i, qos_flow_index, session->param.qos[qos_flow_index].fiveQI);
LOG_I(NR_RRC, "PDU Session ID %d, QOS flow %d, 5QI %d \n", session->pdusession_id, qos_flow_index, fiveQI);
}
session->status = PDU_SESSION_STATUS_DONE;
session->xid = params.transaction_id;
}
byte_array_t msg = do_RRCReconfiguration(&params);
@@ -766,24 +747,44 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t
free_byte_array(msg);
}
//-----------------------------------------------------------------------------
void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
DevAssert(deliver_pdu_data != NULL);
deliver_ue_ctxt_modification_data_t *data = deliver_pdu_data;
data->modification_req->rrc_container = (uint8_t *)buf;
data->modification_req->rrc_container_length = size;
data->rrc->mac_rrc.ue_context_modification_request(data->assoc_id, data->modification_req);
}
void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *ue_p,
uint8_t xid,
uint32_t nas_length,
uint8_t *nas_buffer)
//-----------------------------------------------------------------------------
{
NR_DRB_ToReleaseList_t *DRB_Release_configList2 = CALLOC(sizeof(*DRB_Release_configList2), 1);
NR_DRB_ToReleaseList_t *drb_release_list = calloc_or_fail(1, sizeof(*drb_release_list));
for (int i = 0; i < NB_RB_MAX; i++) {
if ((ue_p->pduSession[i].status == PDU_SESSION_STATUS_TORELEASE) && ue_p->pduSession[i].xid == xid) {
asn1cSequenceAdd(DRB_Release_configList2->list, NR_DRB_Identity_t, DRB_release);
*DRB_release = i + 1;
f1ap_drb_to_be_released_t rel_drbs[32];
int n_rel_drbs = 0;
if (!ue_p->DRB_ReleaseList)
ue_p->DRB_ReleaseList = calloc_or_fail(1, sizeof(*ue_p->DRB_ReleaseList));
FOR_EACH_SEQ_ARR(rrc_pdusession_release_t *, item, ue_p->pduSessions_to_release) {
if (item->xid == xid) {
FOR_EACH_SEQ_ARR(drb_t *, drb, ue_p->drbs) {
if (drb->pdusession_id == item->pdusession_id) {
asn1cSequenceAdd(drb_release_list->list, NR_DRB_Identity_t, DRB_release);
asn1cSequenceAdd(ue_p->DRB_ReleaseList->list, NR_DRB_Identity_t, DRB_release2);
*DRB_release2 = drb->drb_id;
*DRB_release = drb->drb_id;
rel_drbs[n_rel_drbs].rb_id = *DRB_release;
n_rel_drbs++;
}
}
}
}
nr_rrc_reconfig_param_t params = {.drb_release_list = DRB_Release_configList2, .transaction_id = xid};
nr_rrc_reconfig_param_t params = {.drb_release_list = drb_release_list, .transaction_id = xid};
if (nas_length > 0) {
params.dedicated_NAS_msg_list[params.num_nas_msg].buf = nas_buffer;
params.dedicated_NAS_msg_list[params.num_nas_msg++].len = nas_length;
@@ -795,6 +796,28 @@ void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc,
}
LOG_DUMPMSG(NR_RRC, DEBUG_RRC, msg.buf, msg.len, "[MSG] RRC Reconfiguration\n");
LOG_I(NR_RRC, "UE %d: Generate NR_RRCReconfiguration (bytes %ld)\n", ue_p->rrc_ue_id, msg.len);
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
f1ap_ue_context_modif_req_t ue_context_modif_req = {
.gNB_CU_ue_id = ue_p->rrc_ue_id,
.gNB_DU_ue_id = ue_data.secondary_ue,
.plmn.mcc = rrc->configuration.plmn[0].mcc,
.plmn.mnc = rrc->configuration.plmn[0].mnc,
.plmn.mnc_digit_length = rrc->configuration.plmn[0].mnc_digit_length,
.nr_cellid = rrc->nr_cellid,
.servCellId = 0,
.drbs_to_be_released_length = n_rel_drbs,
.drbs_to_be_released = (f1ap_drb_to_be_released_t *)rel_drbs,
};
deliver_ue_ctxt_modification_data_t data = {.rrc = rrc, .modification_req = &ue_context_modif_req, .assoc_id = ue_data.du_assoc_id};
nr_pdcp_data_req_srb(ue_p->rrc_ue_id,
DL_SCH_LCID_DCCH,
rrc_gNB_mui++,
msg.len,
(unsigned char *const)msg.buf,
rrc_deliver_ue_ctxt_modif_req,
&data);
const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration;
nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len);
free_RRCReconfiguration_params(params);
@@ -844,24 +867,19 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
return;
/* loop through active DRBs */
for (int drb_id = 1; drb_id <= MAX_DRBS_PER_UE; drb_id++) {
drb_t *drb = get_drb(ue_p, drb_id);
if (drb->status == DRB_INACTIVE)
drb_t *drb = get_drb(ue_p->drbs, drb_id);
if (!drb)
continue;
/* fetch an existing PDU session for this DRB */
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue_p, drb_id);
pdusession_t *pdu = find_active_pdu_session(ue_p, drb->pdusession_id);
if (pdu == NULL) {
LOG_E(RRC, "UE %d: E1 Bearer Context Modification: no PDU session for DRB ID %d\n", ue_p->rrc_ue_id, drb_id);
continue;
}
/* Get pointer to existing (or new one) PDU session to modify in E1 */
pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, pdu->param.pdusession_id);
AssertError(pdu != NULL,
continue,
"UE %u: E1 Bearer Context Modification: PDU session %d to setup is null\n",
ue_p->rrc_ue_id,
pdu->param.pdusession_id);
pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, drb->pdusession_id);
/* Prepare PDU for E1 Bearear Context Modification Request */
pdu_e1->sessionId = pdu->param.pdusession_id;
pdu_e1->sessionId = drb->pdusession_id;
/* Fill DRB to setup with ID, DL TL and DL TEID */
DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify];
drb_e1->id = drb_id;
@@ -871,9 +889,8 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
/* PDCP configuration */
if (!drb_e1->pdcp_config)
drb_e1->pdcp_config = malloc_or_fail(sizeof(*drb_e1->pdcp_config));
bearer_context_pdcp_config_t *pdcp_config = drb_e1->pdcp_config;
set_bearer_context_pdcp_config(pdcp_config, drb, rrc->configuration.um_on_default_drb);
pdcp_config->pDCP_Reestablishment = true;
*drb_e1->pdcp_config = set_bearer_context_pdcp_config(rrc->pdcp_config, rrc->configuration.um_on_default_drb);
drb_e1->pdcp_config->pDCP_Reestablishment = true;
/* increase DRB to modify counter */
pdu_e1->numDRB2Modify += 1;
}
@@ -1510,23 +1527,36 @@ static int handle_rrcReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE
* the NG-RAN node shall pass it transparently towards the UE.
* - 8.6.2: The NAS-PDU IE contains an AMFUE message that is transferred without interpretation in the NG-RAN node.
*/
void rrc_forward_ue_nas_message(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
void rrc_forward_ue_nas_message(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, byte_array_t *pdu)
{
if (UE->nas_pdu.buf == NULL || UE->nas_pdu.len == 0)
if (pdu->buf == NULL || pdu->len == 0)
return; // no problem: the UE will re-request a NAS PDU
LOG_UE_DL_EVENT(UE, "Send DL Information Transfer [%ld bytes]\n", UE->nas_pdu.len);
LOG_UE_DL_EVENT(UE, "Send DL Information Transfer [%ld bytes]\n", pdu->len);
uint8_t buffer[4096];
unsigned int xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
uint32_t length = do_NR_DLInformationTransfer(buffer, sizeof(buffer), xid, UE->nas_pdu.len, UE->nas_pdu.buf);
uint32_t length = do_NR_DLInformationTransfer(buffer, sizeof(buffer), xid, pdu->len, pdu->buf);
LOG_DUMPMSG(NR_RRC, DEBUG_RRC, buffer, length, "[MSG] RRC DL Information Transfer\n");
rb_id_t srb_id = UE->Srb[2].Active ? DL_SCH_LCID_DCCH1 : DL_SCH_LCID_DCCH;
const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_dlInformationTransfer;
nr_rrc_transfer_protected_rrc_message(rrc, UE, srb_id, msg_id, buffer, length);
// no need to free UE->nas_pdu.buf, do_NR_DLInformationTransfer() did that
UE->nas_pdu.buf = NULL;
UE->nas_pdu.len = 0;
// no need to free pdu->buf, do_NR_DLInformationTransfer() did that
pdu->buf = NULL;
pdu->len = 0;
}
/** @brief Forward stored NAS PDUs received in NGAP messages, i.e. PDU Session/Initial Context Setup Request */
static void rrc_forward_stored_nas_pdus(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
DevAssert(UE->nas_pdu != NULL);
DevAssert(seq_arr_size(UE->nas_pdu) == 1); // Expected only stored NAS PDU
FOR_EACH_SEQ_ARR(byte_array_t *, pdu, UE->nas_pdu) {
rrc_forward_ue_nas_message(rrc, UE, pdu);
}
// Free seq_arr
free(UE->nas_pdu);
UE->nas_pdu = NULL;
}
static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_UECapabilityInformation_t *ue_cap_info)
@@ -1634,12 +1664,12 @@ static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE,
if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) {
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);
rrc_forward_stored_nas_pdus(rrc, UE);
return;
}
} else {
rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE);
rrc_forward_ue_nas_message(rrc, UE);
rrc_forward_stored_nas_pdus(rrc, UE);
}
return;
@@ -1724,9 +1754,12 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
switch (UE->xids[xid]) {
case RRC_PDUSESSION_RELEASE: {
gtpv1u_gnb_delete_tunnel_req_t req = {0};
gtpv1u_delete_ngu_tunnel(rrc->module_id, &req);
// NGAP_PDUSESSION_RELEASE_RESPONSE
FOR_EACH_SEQ_ARR(pdusession_t *, addmod, UE->pduSessions_to_addmod) {
rrc_pdusession_release_t release = {.pdusession_id = addmod->pdusession_id, .xid = xid};
add_pduSession_to_release(&UE->pduSessions_to_release, UE->rrc_ue_id, release);
}
SEQ_ARR_CLEANUP_AND_FREE(UE->pduSessions_to_addmod, free_pdusession);
release_pduSessions(rrc, UE);
rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid);
} break;
case RRC_PDUSESSION_ESTABLISH:
@@ -1736,7 +1769,7 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
UE->n_initial_pdu = 0;
free(UE->initial_pdus);
UE->initial_pdus = NULL;
} else if (UE->nb_of_pdusessions > 0)
} else if (seq_arr_size(UE->pduSessions_to_addmod) > 0)
rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(rrc, UE, xid);
break;
case RRC_PDUSESSION_MODIFY:
@@ -1882,13 +1915,13 @@ static int rrc_gNB_decode_dcch(gNB_RRC_INST *rrc, const f1ap_ul_rrc_message_t *m
if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) {
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);
rrc_forward_stored_nas_pdus(rrc, UE);
}
} else {
/* we already have capabilities, and no PDU sessions to setup, ack
* this UE */
rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE);
rrc_forward_ue_nas_message(rrc, UE);
rrc_forward_stored_nas_pdus(rrc, UE);
}
break;
@@ -2022,7 +2055,7 @@ static void store_du_f1u_tunnel(const f1ap_drb_to_be_setup_t *drbs, int n, gNB_R
const f1ap_drb_to_be_setup_t *drb_f1 = &drbs[i];
AssertFatal(drb_f1->up_dl_tnl_length == 1, "can handle only one UP param\n");
AssertFatal(drb_f1->drb_id < MAX_DRBS_PER_UE, "illegal DRB ID %ld\n", drb_f1->drb_id);
drb_t *drb = get_drb(ue, drb_f1->drb_id);
drb_t *drb = get_drb(ue->drbs, drb_f1->drb_id);
drb->du_tunnel_config = f1u_gtp_update(drb_f1->up_dl_tnl[0].teid, drb_f1->up_dl_tnl[0].tl_address);
}
}
@@ -2044,14 +2077,14 @@ static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f
for (int i = 0; i < n; i++) {
const f1ap_drb_to_be_setup_t *drb_f1 = &drbs[i];
rrc_pdu_session_param_t *pdu_ue = find_pduSession_from_drbId(UE, drb_f1->drb_id);
pdusession_t *pdu_ue = find_pduSession_from_drbId(UE, UE->pduSessions_to_addmod, drb_f1->drb_id);
if (pdu_ue == NULL) {
LOG_E(RRC, "UE %d: UE Context Modif Response: no PDU session for DRB ID %ld\n", UE->rrc_ue_id, drb_f1->drb_id);
continue;
}
pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, pdu_ue->param.pdusession_id);
pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, pdu_ue->pdusession_id);
DevAssert(pdu_e1 != NULL);
pdu_e1->sessionId = pdu_ue->param.pdusession_id;
pdu_e1->sessionId = pdu_ue->pdusession_id;
DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify];
/* Fill E1 bearer context modification */
fill_e1_bearer_modif(drb_e1, drb_f1);
@@ -2103,7 +2136,7 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance
}
if (resp->drbs_to_be_setup_length > 0) {
int num_drb = get_number_active_drbs(UE);
int num_drb = seq_arr_size(UE->drbs);
DevAssert(num_drb == 0 || num_drb == resp->drbs_to_be_setup_length);
/* Note: we would ideally check that SRB2 is acked, but at least LiteOn DU
@@ -2220,9 +2253,7 @@ static void rrc_CU_process_ue_context_release_complete(MessageDef *msg_p)
/* only trigger release if it has been requested by core
* otherwise, it might be CU that requested release on a DU during normal
* operation (i.e, handover) */
uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION];
get_pduSession_array(UE, pdu_sessions);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(rrc->module_id, UE);
rrc_remove_ue(RC.nrrrc[0], ue_context_p);
}
}
@@ -2352,31 +2383,15 @@ unsigned int mask_flip(unsigned int x) {
return((((x>>8) + (x<<8))&0xffff)>>6);
}
pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession)
qos_characteristics_t get_qos_characteristics(const int qfi, pdusession_t *pdu)
{
pdusession_t *pdu = &pduSession->param;
for (int i = 0; i < pdu->nb_qos; i++) {
if (qfi == pdu->qos[i].qfi)
return &pdu->qos[i];
return pdu->qos[i].qos_params.qos_characteristics;
}
AssertFatal(1 == 0, "The pdu session %d does not contain a qos flow with qfi = %d\n", pdu->pdusession_id, qfi);
return NULL;
}
/* \bref return F1AP QoS characteristics based on Qos flow parameters */
f1ap_qos_characteristics_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param)
{
f1ap_qos_characteristics_t qos_char = {0};
if (qos_param->fiveQI_type == DYNAMIC) {
qos_char.qos_type = DYNAMIC;
qos_char.dynamic.fiveqi = qos_param->fiveQI;
qos_char.dynamic.qos_priority_level = qos_param->qos_priority;
} else {
qos_char.qos_type = NON_DYNAMIC;
qos_char.non_dynamic.fiveqi = qos_param->fiveQI;
qos_char.non_dynamic.qos_priority_level = qos_param->qos_priority;
}
return qos_char;
qos_characteristics_t dummy = {0};
return dummy; // unreachable, but avoids compiler warning
}
/* \brief fills a list of DRBs to be setup from a number of PDU sessions in E1
@@ -2389,7 +2404,7 @@ 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 *RRC_pduSession = find_pduSession(UE, pduSession[p].id, false);
pdusession_t *RRC_pduSession = (pdusession_t *)find_pduSession(UE->pduSessions_to_addmod, pduSession[p].id);
DevAssert(RRC_pduSession);
for (int i = 0; i < pduSession[p].numDRBSetup; i++) {
const DRB_nGRAN_setup_t *drb_config = &pduSession[p].DRBnGRanList[i];
@@ -2405,18 +2420,17 @@ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc,
int nb_qos_flows = drb_config->numQosFlowSetup;
AssertFatal(nb_qos_flows > 0, "must map at least one flow to a DRB\n");
drb->drb_info.flows_to_be_setup_length = nb_qos_flows;
drb->drb_info.flows_mapped_to_drb = calloc(nb_qos_flows, sizeof(f1ap_flows_mapped_to_drb_t));
AssertFatal(drb->drb_info.flows_mapped_to_drb, "could not allocate memory\n");
drb->drb_info.flows_mapped_to_drb = calloc_or_fail(nb_qos_flows, sizeof(f1ap_flows_mapped_to_drb_t));
for (int j = 0; j < nb_qos_flows; j++) {
drb->drb_info.flows_mapped_to_drb[j].qfi = drb_config->qosFlows[j].qfi;
pdusession_level_qos_parameter_t *in_qos_char = get_qos_characteristics(drb_config->qosFlows[j].qfi, RRC_pduSession);
drb->drb_info.flows_mapped_to_drb[j].qos_params.qos_characteristics = get_qos_char_from_qos_flow_param(in_qos_char);
f1ap_flows_mapped_to_drb_t *qos2drb = &drb->drb_info.flows_mapped_to_drb[j];
qos2drb->qfi = drb_config->qosFlows[j].qfi;
qos2drb->qos_params.qos_characteristics = get_qos_characteristics(drb_config->qosFlows[j].qfi, RRC_pduSession);
}
/* the DRB QoS parameters: we just reuse the ones from the first flow */
drb->drb_info.drb_qos = drb->drb_info.flows_mapped_to_drb[0].qos_params;
/* pass NSSAI info to MAC */
drb->nssai = RRC_pduSession->param.nssai;
drb->nssai = RRC_pduSession->nssai;
nb_drb++;
}
@@ -2444,19 +2458,19 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp
// save the tunnel address for the PDU sessions
for (int i = 0; i < resp->numPDUSessions; i++) {
pdu_session_setup_t *e1_pdu = &resp->pduSession[i];
rrc_pdu_session_param_t *rrc_pdu = find_pduSession(UE, e1_pdu->id, false);
pdusession_t *rrc_pdu = (pdusession_t *)find_pduSession(UE->pduSessions_to_addmod, e1_pdu->id);
if (rrc_pdu == NULL) {
LOG_W(RRC, "E1: received setup for PDU session %ld, but has not been requested\n", e1_pdu->id);
continue;
}
rrc_pdu->param.n3_outgoing = f1u_gtp_update(e1_pdu->tl_info.teId, e1_pdu->tl_info.tlAddress);
rrc_pdu->n3_outgoing = f1u_gtp_update(e1_pdu->tl_info.teId, e1_pdu->tl_info.tlAddress);
// save the tunnel address for the DRBs
for (int i = 0; i < e1_pdu->numDRBSetup; i++) {
DRB_nGRAN_setup_t *drb_config = &e1_pdu->DRBnGRanList[i];
// numUpParam only relevant in F1, but not monolithic
AssertFatal(drb_config->numUpParam <= 1, "can only up to one UP param\n");
drb_t *drb = get_drb(UE, drb_config->id);
drb_t *drb = get_drb(UE->drbs, drb_config->id);
UP_TL_information_t *tl_info = &drb_config->UpParamList[0].tl_info;
drb->cuup_tunnel_config = f1u_gtp_update(tl_info->teId, tl_info->tlAddress);
}
@@ -2485,7 +2499,7 @@ void rrc_gNB_process_e1_bearer_context_setup_failure(e1ap_bearer_context_setup_f
/**
* @brief E1AP Bearer Context Modification Response processing on CU-CP
*/
void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t *resp)
void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t *resp) // here
{
gNB_RRC_INST *rrc = RC.nrrrc[0];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id);
@@ -2585,22 +2599,6 @@ static void print_rrc_meas(FILE *f, const NR_MeasResults_t *measresults)
}
}
static const char *get_pdusession_status_text(pdu_session_status_t status)
{
switch (status) {
case PDU_SESSION_STATUS_NEW: return "new";
case PDU_SESSION_STATUS_DONE: return "done";
case PDU_SESSION_STATUS_ESTABLISHED: return "established";
case PDU_SESSION_STATUS_REESTABLISHED: return "reestablished";
case PDU_SESSION_STATUS_TOMODIFY: return "to-modify";
case PDU_SESSION_STATUS_FAILED: return "failed";
case PDU_SESSION_STATUS_TORELEASE: return "to-release";
case PDU_SESSION_STATUS_RELEASED: return "released";
default: AssertFatal(false, "illegal PDU status code %d\n", status); return "illegal";
}
return "illegal";
}
static bool write_rrc_stats(const gNB_RRC_INST *rrc)
{
const char *filename = "nrRRC_stats.log";
@@ -2633,11 +2631,14 @@ static bool write_rrc_stats(const gNB_RRC_INST *rrc)
time_t last_seen = now - ue_ctxt->last_seen;
fprintf(f, " last RRC activity: %ld seconds ago\n", last_seen);
if (ue_ctxt->nb_of_pdusessions == 0)
fprintf(f, " (no PDU sessions)\n");
for (int nb_pdu = 0; nb_pdu < ue_ctxt->nb_of_pdusessions; ++nb_pdu) {
const rrc_pdu_session_param_t *pdu = &ue_ctxt->pduSession[nb_pdu];
fprintf(f, " PDU session %d ID %d status %s\n", nb_pdu, pdu->param.pdusession_id, get_pdusession_status_text(pdu->status));
// Active PDU sessions
if (ue_ctxt->pduSessions == NULL || seq_arr_size(ue_ctxt->pduSessions) == 0) {
fprintf(f, " (no active PDU sessions)\n");
} else {
fprintf(f, " %ld active PDU sessions:\n", seq_arr_size(ue_ctxt->pduSessions));
FOR_EACH_SEQ_ARR(pdusession_t *, pdu, ue_ctxt->pduSessions) {
fprintf(f, " - ID %d\n", pdu->pdusession_id);
}
}
fprintf(f, " associated DU: ");

View File

@@ -39,6 +39,8 @@
#include <arpa/inet.h>
#include "E1AP_ConfidentialityProtectionIndication.h"
#include "E1AP_IntegrityProtectionIndication.h"
#include "E1AP_RLC-Mode.h"
#include "NR_PDCP-Config.h"
#include "NGAP_CauseRadioNetwork.h"
#include "NGAP_Dynamic5QIDescriptor.h"
#include "NGAP_GTPTunnel.h"
@@ -82,6 +84,7 @@
#include "rrc_messages_types.h"
#include "s1ap_messages_types.h"
#include "uper_encoder.h"
#include "common/utils/alg/find.h"
#ifdef E2_AGENT
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
@@ -166,10 +169,10 @@ static nr_guami_t get_guami(const uint32_t amf_Id, const plmn_id_t plmn)
static void cp_pdusession_resource_item_to_pdusession(pdusession_t *dst, const pdusession_resource_item_t *src)
{
dst->pdusession_id = src->pdusession_id;
dst->nas_pdu = src->nas_pdu;
dst->nb_qos = src->pdusessionTransfer.nb_qos;
for (uint8_t i = 0; i < src->pdusessionTransfer.nb_qos && i < QOSFLOW_MAX_VALUE; ++i) {
dst->qos[i] = src->pdusessionTransfer.qos[i];
dst->qos[i].qfi = src->pdusessionTransfer.qos[i].qfi;
dst->qos[i].qos_params = src->pdusessionTransfer.qos[i].qos_params;
}
dst->pdu_session_type = src->pdusessionTransfer.pdu_session_type;
dst->n3_incoming = src->pdusessionTransfer.n3_incoming;
@@ -240,6 +243,48 @@ 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 Returns an instance of E1AP DRB To Setup List */
static DRB_nGRAN_to_setup_t fill_drb_ngran_tosetup(const drb_t *rrc_drb, const pdusession_t *session, const gNB_RRC_INST *rrc)
{
DRB_nGRAN_to_setup_t drb_ngran = {0};
drb_ngran.id = rrc_drb->drb_id;
drb_ngran.sdap_config.defaultDRB = true;
drb_ngran.sdap_config.sDAP_Header_UL = rrc->configuration.enable_sdap ? 0 : 1;
drb_ngran.sdap_config.sDAP_Header_DL = rrc->configuration.enable_sdap ? 0 : 1;
drb_ngran.pdcp_config = set_bearer_context_pdcp_config(rrc->pdcp_config, rrc->configuration.um_on_default_drb);
drb_ngran.numCellGroups = 1;
for (int k = 0; k < drb_ngran.numCellGroups; k++) {
drb_ngran.cellGroupList[k] = MCG; // 1 cellGroup only
}
drb_ngran.numQosFlow2Setup = session->nb_qos;
for (int k = 0; k < drb_ngran.numQosFlow2Setup; k++) {
qos_flow_to_setup_t *qos_flow = &drb_ngran.qosFlows[k];
qos_flow->qfi = session->qos[k].qfi;
qos_flow->qos_params = session->qos[k].qos_params;
}
return drb_ngran;
}
/** @brief Set up the drb_t DRB instance in the UE context */
static drb_t *setup_rrc_drb_for_pdu_session(gNB_RRC_UE_t *ue, const pdusession_t *session)
{
int drb_id = seq_arr_size(ue->drbs) + 1;
if (drb_id >= MAX_DRBS_PER_UE) {
LOG_E(NR_RRC, "UE %d: Cannot set up new DRB for pdusession_id=%d - reached maximum capacity\n", ue->rrc_ue_id, session->pdusession_id);
return NULL;
}
LOG_I(NR_RRC, "UE %d: add DRB ID %d (pdusession_id=%d)\n", ue->rrc_ue_id, drb_id, session->pdusession_id);
drb_t drb = {.drb_id = drb_id, .pdusession_id = session->pdusession_id};
return add_rrc_drb(&ue->drbs, drb);
}
/**
* @brief Triggers bearer setup for the specified UE.
*
@@ -265,9 +310,16 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession
e1ap_nssai_t cuup_nssai = {0};
for (int i = 0; i < n; i++) {
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, sessions[i].pdusession_id, true);
pdusession_t *session = &pduSession->param;
cp_pdusession(session, &sessions[i]);
pdusession_t *session = add_pduSession(&UE->pduSessions_to_addmod, UE->rrc_ue_id, &sessions[i]);
if (!session) {
LOG_E(NR_RRC, "Could not add PDU Session for UE %d\n", UE->rrc_ue_id);
return false;
}
LOG_I(NR_RRC,
"Added item ID %d to pduSessions_to_addmod (total = %ld)\n",
session->pdusession_id,
seq_arr_size(UE->pduSessions_to_addmod));
// Fill E1 Bearer Context Modification Request
bearer_req.gNB_cu_cp_ue_id = UE->rrc_ue_id;
security_information_t *secInfo = &bearer_req.secInfo;
secInfo->cipheringAlgorithm = rrc->security.do_drb_ciphering ? UE->ciphering_algorithm : 0;
@@ -294,60 +346,14 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession
inet_ntop(AF_INET, n3_incoming->addr.buffer, ip_str, sizeof(ip_str));
LOG_I(NR_RRC, "Bearer Context Setup: PDU Session ID=%d, incoming TEID=0x%08x, Addr=%s\n", session->pdusession_id, n3_incoming->teid, ip_str);
/* we assume for the moment one DRB per PDU session. Activate the bearer,
* and configure in RRC. */
int drb_id = get_next_available_drb_id(UE);
drb_t *rrc_drb = generateDRB(UE,
drb_id,
pduSession,
rrc->configuration.enable_sdap,
rrc->security.do_drb_integrity,
rrc->security.do_drb_ciphering,
&rrc->pdcp_config);
pdu->numDRB2Setup = 1; // One DRB per PDU Session. TODO: Remove hardcoding
for (int j=0; j < pdu->numDRB2Setup; j++) {
DRB_nGRAN_to_setup_t *drb = pdu->DRBnGRanList + j;
drb->id = rrc_drb->drb_id;
/* SDAP */
struct sdap_config_s *sdap_config = &rrc_drb->cnAssociation.sdap_config;
drb->sdap_config.defaultDRB = sdap_config->defaultDRB;
drb->sdap_config.sDAP_Header_UL = sdap_config->sdap_HeaderUL;
drb->sdap_config.sDAP_Header_DL = sdap_config->sdap_HeaderDL;
/* PDCP */
set_bearer_context_pdcp_config(&drb->pdcp_config, rrc_drb, rrc->configuration.um_on_default_drb);
drb->numCellGroups = 1; // assume one cell group associated with a DRB
// Set all Cell Group IDs to MCG
for (int k=0; k < drb->numCellGroups; k++) {
cell_group_id_t *cellGroup = drb->cellGroupList + k;
*cellGroup = MCG;
}
drb->numQosFlow2Setup = session->nb_qos;
for (int k=0; k < drb->numQosFlow2Setup; k++) {
qos_flow_to_setup_t *qos_flow = drb->qosFlows + k;
pdusession_level_qos_parameter_t *qos_session = session->qos + k;
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 *rent_priority = &qos_flow->qos_params.alloc_reten_priority;
ngap_allocation_retention_priority_t *rent_priority_in = &qos_session->allocation_retention_priority;
rent_priority->priority_level = rent_priority_in->priority_level;
rent_priority->preemption_capability = rent_priority_in->pre_emp_capability;
rent_priority->preemption_vulnerability = rent_priority_in->pre_emp_vulnerability;
for (int j = 0; j < pdu->numDRB2Setup; j++) {
drb_t *rrc_drb = setup_rrc_drb_for_pdu_session(UE, session);
if (!rrc_drb) {
LOG_E(RRC, "Failed to allocate DRB for PDU session ID %d\n", session->pdusession_id);
return false;
}
pdu->DRBnGRanList[0] = fill_drb_ngran_tosetup(rrc_drb, session, rrc);
}
}
/* Limitation: we assume one fixed CU-UP per UE. We base the selection on
@@ -392,6 +398,15 @@ static void send_ngap_initial_context_setup_resp_fail(instance_t instance,
itti_send_msg_to_task(TASK_NGAP, instance, msg_p);
}
/** @brief Add NAS PDU to seq_arr list, to be forwarded with an RRC message, e.g.
* with RRCReconfiguration message (TS 38.331, SEQUENCE (SIZE(1..maxDRB)) OF DedicatedNAS-Message)
* or with a DL information transfer message (1 dedicatedNAS-Message) */
static byte_array_t *store_nas_pdu(gNB_RRC_UE_t *UE, byte_array_t pdu)
{
SEQ_ARR_INIT(&UE->nas_pdu, byte_array_t, MAX_DRBS_PER_UE);
return SEQ_ARR_PUSH_BACK_AND_GET(byte_array_t, UE->nas_pdu, &pdu);
}
//------------------------------------------------------------------------------
int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t instance)
//------------------------------------------------------------------------------
@@ -416,17 +431,18 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t
// Directly copy the entire guami structure
UE->ue_guami = req->guami;
/* NAS PDU */
// this is malloced pointers, we pass it for later free()
UE->nas_pdu = req->nas_pdu;
if (req->nb_of_pdusessions > 0) {
/* 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)
// Store NAS PDU, forward later
for (int i = 0; i < UE->n_initial_pdu; ++i) {
cp_pdusession_resource_item_to_pdusession(&UE->initial_pdus[i], &req->pdusession[i]);
if (req->pdusession[i].nas_pdu.len > 0) {
store_nas_pdu(UE, req->pdusession[i].nas_pdu);
}
}
}
/* security */
@@ -441,6 +457,8 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t
/* configure only integrity, ciphering comes after receiving SecurityModeComplete */
nr_rrc_pdcp_config_security(UE, false);
rrc_gNB_generate_SecurityModeCommand(rrc, UE);
// Store NAS PDU, forward later
store_nas_pdu(UE, req->nas_pdu);
} else {
/* if AS security key is active, we also have the UE capabilities. Then,
* there are two possibilities: we should set up PDU sessions, and/or
@@ -453,14 +471,16 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t
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);
rrc_forward_ue_nas_message(rrc, UE);
// Forward directly NAS PDU
rrc_forward_ue_nas_message(rrc, UE, &req->nas_pdu);
return -1;
}
} else {
/* no PDU sesion to setup: acknowledge this message, and forward NAS
* message, if required */
rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE);
rrc_forward_ue_nas_message(rrc, UE);
// Forward directly NAS PDU
rrc_forward_ue_nas_message(rrc, UE, &req->nas_pdu);
}
}
@@ -480,39 +500,52 @@ static gtpu_tunnel_t cp_gtp_tunnel(const gtpu_tunnel_t in)
return out;
}
/** @brief Fill NGAP PDU Session Resource Setup item from RRC PDU Session item */
static pdusession_setup_t fill_pdusession_setup(pdusession_t *session)
{
pdusession_setup_t setup = {0};
setup.pdusession_id = session->pdusession_id;
setup.pdu_session_type = session->pdu_session_type;
setup.n3_outgoing = cp_gtp_tunnel(session->n3_outgoing);
setup.nb_of_qos_flow = session->nb_qos;
for (int qos_flow_index = 0; qos_flow_index < session->nb_qos; qos_flow_index++) {
setup.associated_qos_flows[qos_flow_index].qfi = session->qos[qos_flow_index].qfi;
setup.associated_qos_flows[qos_flow_index].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
}
char ip_str[INET_ADDRSTRLEN] = {0};
inet_ntop(AF_INET, setup.n3_outgoing.addr.buffer, ip_str, sizeof(ip_str));
LOG_I(NR_RRC, "PDU Session Resource Setup item: ID=%d, outgoing TEID=0x%08x, Addr=%s\n",
setup.pdusession_id,
setup.n3_outgoing.teid,
ip_str);
return setup;
}
void rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
MessageDef *msg_p = NULL;
int pdu_sessions_done = 0;
int pdu_sessions_failed = 0;
msg_p = itti_alloc_new_message (TASK_RRC_ENB, rrc->module_id, NGAP_INITIAL_CONTEXT_SETUP_RESP);
ngap_initial_context_setup_resp_t *resp = &NGAP_INITIAL_CONTEXT_SETUP_RESP(msg_p);
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
for (int pdusession = 0; pdusession < UE->nb_of_pdusessions; pdusession++) {
rrc_pdu_session_param_t *session = &UE->pduSession[pdusession];
if (session->status == PDU_SESSION_STATUS_DONE) {
pdu_sessions_done++;
resp->pdusessions[pdusession].pdusession_id = session->param.pdusession_id;
resp->pdusessions[pdusession].n3_outgoing = cp_gtp_tunnel(session->param.n3_outgoing);
resp->pdusessions[pdusession].nb_of_qos_flow = session->param.nb_qos;
for (int qos_flow_index = 0; qos_flow_index < session->param.nb_qos; qos_flow_index++) {
resp->pdusessions[pdusession].associated_qos_flows[qos_flow_index].qfi = session->param.qos[qos_flow_index].qfi;
resp->pdusessions[pdusession].associated_qos_flows[qos_flow_index].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
}
} else if (session->status != PDU_SESSION_STATUS_ESTABLISHED) {
session->status = PDU_SESSION_STATUS_FAILED;
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID};
fill_pdu_session_resource_failed_to_setup_item(&resp->pdusessions_failed[pdu_sessions_failed],
session->param.pdusession_id,
cause);
pdu_sessions_failed++;
}
FOR_EACH_SEQ_ARR(pdusession_t *, session, UE->pduSessions_to_addmod) {
resp->pdusessions[resp->nb_of_pdusessions++] = fill_pdusession_setup(session);
// Add PDU Session to setup list
session->xid = -1; // reset xid
add_pduSession(&UE->pduSessions, UE->rrc_ue_id, session);
LOG_I(NR_RRC, "Added item ID %d to pduSessions list, (total = %ld)\n", session->pdusession_id, seq_arr_size(UE->pduSessions));
}
resp->nb_of_pdusessions = pdu_sessions_done;
resp->nb_of_pdusessions_failed = pdu_sessions_failed;
FOR_EACH_SEQ_ARR(rrc_pdusession_failed_t *, session, UE->pduSessions_failed) {
pdusession_failed_t *fail = &resp->pdusessions_failed[resp->nb_of_pdusessions_failed++];
fail->cause = session->cause;
fail->pdusession_id = session->pdusession_id;
}
SEQ_ARR_CLEANUP_AND_FREE(UE->pduSessions_to_addmod, free_pdusession);
SEQ_ARR_CLEANUP_AND_FREE(UE->pduSessions_failed, NULL);
itti_send_msg_to_task (TASK_NGAP, rrc->module_id, msg_p);
}
@@ -618,9 +651,9 @@ static void set_UE_security_algos(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, con
integrityProtAlgorithm);
}
//------------------------------------------------------------------------------
/** @brief Process Downlink NAS transport (AMF to NG-RAN) (8.6.2 3GPP TS 38.413). The goal of this
* message is to forward a NAS message transparently via the NG-RAN node to the UE */
int rrc_gNB_process_NGAP_DOWNLINK_NAS(MessageDef *msg_p, instance_t instance, mui_t *rrc_gNB_mui)
//------------------------------------------------------------------------------
{
ngap_downlink_nas_t *req = &NGAP_DOWNLINK_NAS(msg_p);
gNB_RRC_INST *rrc = RC.nrrrc[instance];
@@ -639,9 +672,11 @@ int rrc_gNB_process_NGAP_DOWNLINK_NAS(MessageDef *msg_p, instance_t instance, mu
return (-1);
}
/* The NAS-PDU IE contains an AMF-UE message that is transferred
without interpretation in the NG-RAN node (8.6.2 3GPP TS 38.413) */
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
UE->nas_pdu = req->nas_pdu;
rrc_forward_ue_nas_message(rrc, UE);
// Forward directly NAS PDU
rrc_forward_ue_nas_message(rrc, UE, &req->nas_pdu);
return 0;
}
@@ -673,53 +708,41 @@ void rrc_gNB_send_NGAP_UPLINK_NAS(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_
void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid)
{
MessageDef *msg_p;
int pdu_sessions_done = 0;
int pdu_sessions_failed = 0;
msg_p = itti_alloc_new_message (TASK_RRC_GNB, rrc->module_id, NGAP_PDUSESSION_SETUP_RESP);
ngap_pdusession_setup_resp_t *resp = &NGAP_PDUSESSION_SETUP_RESP(msg_p);
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
for (int pdusession = 0; pdusession < UE->nb_of_pdusessions; pdusession++) {
rrc_pdu_session_param_t *session = &UE->pduSession[pdusession];
if (session->status == PDU_SESSION_STATUS_DONE) {
pdusession_setup_t *tmp = &resp->pdusessions[pdu_sessions_done];
tmp->pdusession_id = session->param.pdusession_id;
tmp->nb_of_qos_flow = session->param.nb_qos;
tmp->n3_outgoing = cp_gtp_tunnel(session->param.n3_outgoing);
tmp->pdu_session_type = session->param.pdu_session_type;
for (int qos_flow_index = 0; qos_flow_index < tmp->nb_of_qos_flow; qos_flow_index++) {
tmp->associated_qos_flows[qos_flow_index].qfi = session->param.qos[qos_flow_index].qfi;
tmp->associated_qos_flows[qos_flow_index].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
}
session->status = PDU_SESSION_STATUS_ESTABLISHED;
char ip_str[INET_ADDRSTRLEN] = {0};
inet_ntop(AF_INET, tmp->n3_outgoing.addr.buffer, ip_str, sizeof(ip_str));
LOG_I(NR_RRC, "PDU Session Setup Response: ID=%d, outgoing TEID=0x%08x, Addr=%s\n", tmp->pdusession_id, tmp->n3_outgoing.teid, ip_str);
pdu_sessions_done++;
} else if (session->status != PDU_SESSION_STATUS_ESTABLISHED) {
session->status = PDU_SESSION_STATUS_FAILED;
pdusession_failed_t *fail = &resp->pdusessions_failed[pdu_sessions_failed];
fail->pdusession_id = session->param.pdusession_id;
fail->cause.type = NGAP_CAUSE_RADIO_NETWORK;
fail->cause.value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID;
pdu_sessions_failed++;
FOR_EACH_SEQ_ARR(pdusession_t *, session, UE->pduSessions_to_addmod) {
if (xid != session->xid) {
LOG_W(NR_RRC, "%s: transaction ID = %d does not match the stored one (PDU Session %d, xid=%d) \n ",
__func__,
xid,
session->pdusession_id,
session->xid);
continue;
}
resp->nb_of_pdusessions = pdu_sessions_done;
resp->nb_of_pdusessions_failed = pdu_sessions_failed;
resp->pdusessions[resp->nb_of_pdusessions++] = fill_pdusession_setup(session);
// Add PDU Session to setup list
session->xid = -1; // reset xid
add_pduSession(&UE->pduSessions, UE->rrc_ue_id, session);
LOG_I(NR_RRC, "Added item ID %d to pduSessions list, (total = %ld)\n", session->pdusession_id, seq_arr_size(UE->pduSessions));
}
if ((pdu_sessions_done > 0 || pdu_sessions_failed)) {
FOR_EACH_SEQ_ARR(rrc_pdusession_failed_t *, session, UE->pduSessions_failed) {
pdusession_failed_t *fail = &resp->pdusessions_failed[resp->nb_of_pdusessions_failed++];
fail->pdusession_id = session->pdusession_id;
fail->cause = session->cause;
}
SEQ_ARR_CLEANUP_AND_FREE(UE->pduSessions_to_addmod, free_pdusession);
SEQ_ARR_CLEANUP_AND_FREE(UE->pduSessions_failed, NULL);
if ((resp->nb_of_pdusessions > 0 || resp->nb_of_pdusessions_failed)) {
LOG_I(NR_RRC, "NGAP_PDUSESSION_SETUP_RESP: sending the message\n");
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
}
for(int i = 0; i < NB_RB_MAX; i++) {
UE->pduSession[i].xid = -1;
}
return;
}
@@ -787,7 +810,7 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins
LOG_W(NR_RRC, "[gNB %ld] In NGAP_PDUSESSION_SETUP_REQ: unknown UE NGAP ID (%u)\n", instance, msg->gNB_ue_ngap_id);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_LOCAL_UE_NGAP_ID};
send_ngap_pdu_session_setup_resp_fail(instance, msg, cause);
rrc_forward_ue_nas_message(rrc, UE);
// rrc_forward_ue_nas_message(rrc, UE); TODO TBC remove: nothing to forward here?
return;
}
@@ -796,7 +819,7 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins
LOG_E(NR_RRC, "UE %d: no security context active for UE, rejecting PDU Session Resource Setup Request\n", UE->rrc_ue_id);
ngap_cause_t cause = {.type = NGAP_CAUSE_PROTOCOL, .value = NGAP_CAUSE_PROTOCOL_MSG_NOT_COMPATIBLE_WITH_RECEIVER_STATE};
send_ngap_pdu_session_setup_resp_fail(instance, msg, cause);
rrc_forward_ue_nas_message(rrc, UE);
// rrc_forward_ue_nas_message(rrc, UE); TODO TBC remove: nothing to forward here?
return;
}
@@ -805,12 +828,12 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins
// might be more work than is worth it. See 8.2.1.4 in 38.413
for (int i = 0; i < msg->nb_pdusessions_tosetup; ++i) {
const pdusession_resource_item_t *p = &msg->pdusession[i];
rrc_pdu_session_param_t *exist = find_pduSession(UE, p->pdusession_id, false /* don't create */);
pdusession_t *exist = (pdusession_t *)find_pduSession(UE->pduSessions, p->pdusession_id);
if (exist) {
LOG_E(NR_RRC, "UE %d: already has existing PDU session %d rejecting PDU Session Resource Setup Request\n", UE->rrc_ue_id, p->pdusession_id);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_MULTIPLE_PDU_SESSION_ID_INSTANCES};
send_ngap_pdu_session_setup_resp_fail(instance, msg, cause);
rrc_forward_ue_nas_message(rrc, UE);
// rrc_forward_ue_nas_message(rrc, UE); TODO TBC remove: nothing to forward here?
return;
}
}
@@ -838,15 +861,20 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins
}
pdusession_t to_setup[NGAP_MAX_PDU_SESSION] = {0};
for (int i = 0; i < msg->nb_pdusessions_tosetup; ++i)
for (int i = 0; i < msg->nb_pdusessions_tosetup; ++i) {
cp_pdusession_resource_item_to_pdusession(&to_setup[i], &msg->pdusession[i]);
// Store NAS PDU, forward later
if (msg->pdusession[i].nas_pdu.len > 0) {
store_nas_pdu(UE, msg->pdusession[i].nas_pdu);
}
}
if (!trigger_bearer_setup(rrc, UE, msg->nb_pdusessions_tosetup, to_setup, msg->ueAggMaxBitRate.br_dl)) {
// 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};
send_ngap_pdu_session_setup_resp_fail(instance, msg, cause);
rrc_forward_ue_nas_message(rrc, UE);
// rrc_forward_ue_nas_message(rrc, UE); TODO TBC remove: nothing to forward here?
} else {
UE->ongoing_pdusession_setup_request = true;
}
@@ -868,59 +896,36 @@ int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t ins
return (-1);
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
bool all_failed = true;
MessageDef *msg_fail_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_MODIFY_RESP);
ngap_pdusession_modify_resp_t *msg = &NGAP_PDUSESSION_MODIFY_RESP(msg_fail_p);
for (int i = 0; i < req->nb_pdusessions_tomodify; i++) {
rrc_pdu_session_param_t *sess;
const pdusession_resource_item_t *sessMod = req->pdusession + i;
for (sess = UE->pduSession; sess < UE->pduSession + UE->nb_of_pdusessions; sess++)
if (sess->param.pdusession_id == sessMod->pdusession_id)
break;
if (sess == UE->pduSession + UE->nb_of_pdusessions) {
pdusession_t *session = (pdusession_t *)find_pduSession(UE->pduSessions, sessMod->pdusession_id);
if (session == NULL) {
LOG_W(NR_RRC, "Requested modification of non-existing PDU session, refusing modification\n");
UE->nb_of_pdusessions++;
sess->status = PDU_SESSION_STATUS_FAILED;
sess->param.pdusession_id = sessMod->pdusession_id;
sess->cause.type = NGAP_CAUSE_RADIO_NETWORK;
UE->pduSession[i].cause.type = NGAP_CAUSE_RADIO_NETWORK;
UE->pduSession[i].cause.value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID;
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID};
pdusession_failed_t *fail = &msg->pdusessions_failed[msg->nb_of_pdusessions_failed++];
fail->pdusession_id = session->pdusession_id;
fail->cause = cause;
} else {
all_failed = false;
sess->status = PDU_SESSION_STATUS_NEW;
sess->param.pdusession_id = sessMod->pdusession_id;
sess->cause.type = NGAP_CAUSE_RADIO_NETWORK;
sess->cause.value = NGAP_CAUSE_RADIO_NETWORK_MULTIPLE_PDU_SESSION_ID_INSTANCES;
sess->status = PDU_SESSION_STATUS_NEW;
sess->param.pdusession_id = sessMod->pdusession_id;
sess->cause.type = NGAP_CAUSE_NOTHING;
if (sessMod->nas_pdu.buf != NULL) {
UE->pduSession[i].param.nas_pdu = sessMod->nas_pdu;
pdusession_t to_mod = {0};
cp_pdusession_resource_item_to_pdusession(&to_mod, &req->pdusession[i]);
add_pduSession(&UE->pduSessions_to_addmod, UE->rrc_ue_id, &to_mod);
LOG_I(NR_RRC, "Added pduSessions_to_addmod %d, (total = %ld)\n", to_mod.pdusession_id, seq_arr_size(UE->pduSessions_to_addmod));
// Store NAS PDU, forward later
if (req->pdusession[i].nas_pdu.len > 0) {
store_nas_pdu(UE, req->pdusession[i].nas_pdu);
}
for (int i = 0; i < req->nb_pdusessions_tomodify; ++i)
cp_pdusession_resource_item_to_pdusession(&UE->pduSession[i].param, &req->pdusession[i]);
}
}
if (!all_failed) {
if (seq_arr_size(UE->pduSessions_to_addmod)) {
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);
}
ngap_pdusession_modify_resp_t *msg = &NGAP_PDUSESSION_MODIFY_RESP(msg_fail_p);
} else if (msg->nb_of_pdusessions_failed > 0) {
LOG_I(NR_RRC, "NGAP PDU Session Modify failure, no PDU Session to modify found in UE context\n");
msg->gNB_ue_ngap_id = req->gNB_ue_ngap_id;
msg->nb_of_pdusessions = 0;
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
if (UE->pduSession[i].status == PDU_SESSION_STATUS_FAILED) {
msg->pdusessions_failed[i].pdusession_id = UE->pduSession[i].param.pdusession_id;
msg->pdusessions_failed[i].cause.type = UE->pduSession[i].cause.type;
msg->pdusessions_failed[i].cause.value = UE->pduSession[i].cause.value;
}
}
itti_send_msg_to_task(TASK_NGAP, instance, msg_fail_p);
}
return (0);
@@ -929,8 +934,6 @@ int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t ins
int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid)
{
MessageDef *msg_p = NULL;
uint8_t pdu_sessions_failed = 0;
uint8_t pdu_sessions_done = 0;
msg_p = itti_alloc_new_message (TASK_RRC_GNB, rrc->module_id, NGAP_PDUSESSION_MODIFY_RESP);
if (msg_p == NULL) {
@@ -942,63 +945,39 @@ int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
if (xid != UE->pduSession[i].xid) {
LOG_W(NR_RRC, "xid does not correspond (context pdu session index %d, status %d, xid %d/%d) \n ", i, UE->pduSession[i].status, xid, UE->pduSession[i].xid);
FOR_EACH_SEQ_ARR(pdusession_t *, session, UE->pduSessions_to_addmod) {
if (xid != session->xid) {
LOG_W(NR_RRC, "%s: transaction ID = %d does not match the stored one (PDU Session %d, xid=%d) \n ",
__func__,
xid,
session->pdusession_id,
session->xid);
continue;
}
if (UE->pduSession[i].status == PDU_SESSION_STATUS_DONE) {
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, UE->pduSession[i].param.pdusession_id, false);
if (pduSession) {
LOG_I(NR_RRC, "update pdu session %d \n", pduSession->param.pdusession_id);
// Update UE->pduSession
pduSession->status = PDU_SESSION_STATUS_ESTABLISHED;
pduSession->cause.type = NGAP_CAUSE_NOTHING;
for (int qos_flow_index = 0; qos_flow_index < UE->pduSession[i].param.nb_qos; qos_flow_index++) {
pduSession->param.qos[qos_flow_index] = UE->pduSession[i].param.qos[qos_flow_index];
}
resp->pdusessions[pdu_sessions_done].pdusession_id = UE->pduSession[i].param.pdusession_id;
for (int qos_flow_index = 0; qos_flow_index < UE->pduSession[i].param.nb_qos; qos_flow_index++) {
resp->pdusessions[pdu_sessions_done].qos[qos_flow_index].qfi = UE->pduSession[i].param.qos[qos_flow_index].qfi;
}
resp->pdusessions[pdu_sessions_done].pdusession_id = UE->pduSession[i].param.pdusession_id;
resp->pdusessions[pdu_sessions_done].nb_of_qos_flow = UE->pduSession[i].param.nb_qos;
LOG_I(NR_RRC,
"Modify Resp (msg index %d, pdu session index %d, status %d, xid %d): nb_of_pduSessions %d, pdusession_id %d \n ",
pdu_sessions_done,
i,
UE->pduSession[i].status,
xid,
UE->nb_of_pdusessions,
resp->pdusessions[pdu_sessions_done].pdusession_id);
pdu_sessions_done++;
} else {
LOG_W(NR_RRC, "PDU SESSION modify of a not existing pdu session %d \n", UE->pduSession[i].param.pdusession_id);
resp->pdusessions_failed[pdu_sessions_failed].pdusession_id = UE->pduSession[i].param.pdusession_id;
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID};
resp->pdusessions_failed[pdu_sessions_failed].cause = cause;
pdu_sessions_failed++;
}
} else if ((UE->pduSession[i].status == PDU_SESSION_STATUS_NEW) || (UE->pduSession[i].status == PDU_SESSION_STATUS_ESTABLISHED)) {
LOG_D(NR_RRC, "PDU SESSION is NEW or already ESTABLISHED\n");
} else if (UE->pduSession[i].status == PDU_SESSION_STATUS_FAILED) {
resp->pdusessions_failed[pdu_sessions_failed].pdusession_id = UE->pduSession[i].param.pdusession_id;
resp->pdusessions_failed[pdu_sessions_failed].cause.type = UE->pduSession[i].cause.type;
resp->pdusessions_failed[pdu_sessions_failed].cause.value = UE->pduSession[i].cause.value;
pdu_sessions_failed++;
pdusession_modify_t *mod = &resp->pdusessions[resp->nb_of_pdusessions++];
LOG_I(NR_RRC, "Send PDU Sesssion Modify Response for pdusession_id=%d \n ", mod->pdusession_id);
mod->pdusession_id = session->pdusession_id;
for (int qos_flow_index = 0; qos_flow_index < session->nb_qos; qos_flow_index++) {
mod->qos[qos_flow_index].qfi = session->qos[qos_flow_index].qfi;
}
mod->pdusession_id = session->pdusession_id;
mod->nb_of_qos_flow = session->nb_qos;
if (!update_pduSession(&UE->pduSessions, session)){
LOG_E(NR_RRC, "Failed to update modified PDU Session %d\n", session->pdusession_id);
return (-1);
}
else
LOG_W(NR_RRC,
"Modify pdu session %d, unknown state %d \n ",
UE->pduSession[i].param.pdusession_id,
UE->pduSession[i].status);
}
SEQ_ARR_CLEANUP_AND_FREE(UE->pduSessions_to_addmod, free_pdusession);
resp->nb_of_pdusessions = pdu_sessions_done;
resp->nb_of_pdusessions_failed = pdu_sessions_failed;
FOR_EACH_SEQ_ARR(rrc_pdusession_failed_t *, session, UE->pduSessions_failed) {
pdusession_failed_t *fail = &resp->pdusessions_failed[resp->nb_of_pdusessions_failed++];
fail->pdusession_id = fail->pdusession_id;
fail->cause = session->cause;
}
SEQ_ARR_CLEANUP_AND_FREE(UE->pduSessions_failed, NULL);
if (pdu_sessions_done > 0 || pdu_sessions_failed > 0) {
LOG_D(NR_RRC, "NGAP_PDUSESSION_MODIFY_RESP: sending the message (total pdu session %d)\n", UE->nb_of_pdusessions);
if (resp->nb_of_pdusessions > 0 || resp->nb_of_pdusessions_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);
@@ -1021,11 +1000,9 @@ void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(const module_id_t gnb_mod_idP,
ngap_ue_release_req_t *req = &NGAP_UE_CONTEXT_RELEASE_REQ(msg);
memset(req, 0, sizeof(*req));
req->gNB_ue_ngap_id = UE->rrc_ue_id;
req->cause.type = causeP.type;
req->cause.value = causeP.value;
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
req->pdusessions[i].pdusession_id = UE->pduSession[i].param.pdusession_id;
req->nb_of_pdusessions++;
req->cause = causeP;
FOR_EACH_SEQ_ARR(pdusession_t *, session, UE->pduSessions) {
req->pdusessions[req->nb_of_pdusessions++].pdusession_id = session->pdusession_id;
}
itti_send_msg_to_task(TASK_NGAP, GNB_MODULE_ID_TO_INSTANCE(gnb_mod_idP), msg);
}
@@ -1076,7 +1053,7 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_
LOG_W(NR_RRC, "[gNB %ld] In NGAP_UE_CONTEXT_RELEASE_COMMAND: unknown UE from gNB_ue_ngap_id (%u)\n",
instance,
gNB_ue_ngap_id);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance, gNB_ue_ngap_id, 0, NULL);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance, &ue_context_p->ue_context);
return -1;
}
@@ -1107,25 +1084,24 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_
/* UE will be freed after UE context release complete */
} else {
// the DU is offline already
uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION];
get_pduSession_array(UE, pdu_sessions);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(rrc->module_id, UE);
rrc_remove_ue(rrc, ue_context_p);
}
return 0;
}
void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance,
uint32_t gNB_ue_ngap_id,
int num_pdu,
uint32_t pdu_session_id[256])
void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, gNB_RRC_UE_t *UE)
{
MessageDef *msg = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UE_CONTEXT_RELEASE_COMPLETE);
NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg).gNB_ue_ngap_id = gNB_ue_ngap_id;
NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg).num_pdu_sessions = num_pdu;
for (int i = 0; i < num_pdu; ++i)
NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg).pdu_session_id[i] = pdu_session_id[i];
ngap_ue_release_complete_t *m = &NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg);
int num_pdu = seq_arr_size(UE->pduSessions);
FOR_EACH_SEQ_ARR(pdusession_t *, session, UE->pduSessions) {
m->gNB_ue_ngap_id = UE->rrc_ue_id;
m->num_pdu_sessions = num_pdu;
for (int i = 0; i < num_pdu; ++i)
NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg).pdu_session_id[i] = session->pdusession_id;
}
itti_send_msg_to_task(TASK_NGAP, instance, msg);
}
@@ -1171,27 +1147,31 @@ void rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE,
void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid)
{
int pdu_sessions_released = 0;
MessageDef *msg_p;
msg_p = itti_alloc_new_message (TASK_RRC_GNB, rrc->module_id, NGAP_PDUSESSION_RELEASE_RESPONSE);
ngap_pdusession_release_resp_t *resp = &NGAP_PDUSESSION_RELEASE_RESPONSE(msg_p);
memset(resp, 0, sizeof(*resp));
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
if (xid == UE->pduSession[i].xid) {
resp->pdusession_release[pdu_sessions_released].pdusession_id = UE->pduSession[i].param.pdusession_id;
pdu_sessions_released++;
//clear
memset(&UE->pduSession[i], 0, sizeof(*UE->pduSession));
UE->pduSession[i].status = PDU_SESSION_STATUS_RELEASED;
LOG_W(NR_RRC, "Released pdu session, but code to finish to free memory\n");
FOR_EACH_SEQ_ARR(rrc_pdusession_release_t *, session, UE->pduSessions_to_release) {
if (xid == session->xid) {
resp->pdusession_release[resp->nb_of_pdusessions_released++].pdusession_id = session->pdusession_id;
LOG_I(NR_RRC, "UE %u: PDU Session %d Release Response\n", resp->gNB_ue_ngap_id, session->pdusession_id);
}
remove_drbs_by_pdu_session(&UE->drbs, session->pdusession_id);
}
SEQ_ARR_CLEANUP_AND_FREE(UE->pduSessions_to_release, NULL);
FOR_EACH_SEQ_ARR(rrc_pdusession_failed_t *, session, UE->pduSessions_failed) {
if (xid == session->xid) {
pdusession_failed_t *fail = &resp->pdusessions_failed[resp->nb_of_pdusessions_failed++];
fail->pdusession_id = session->pdusession_id;
fail->cause = session->cause;
LOG_I(NR_RRC, "UE %u: PDU Session %d Failed to Release\n", resp->gNB_ue_ngap_id, fail->pdusession_id);
}
}
SEQ_ARR_CLEANUP_AND_FREE(UE->pduSessions_failed, NULL);
resp->nb_of_pdusessions_released = pdu_sessions_released;
resp->nb_of_pdusessions_failed = 0;
LOG_I(NR_RRC, "NGAP PDUSESSION RELEASE RESPONSE: rrc_ue_id %u release_pdu_sessions %d\n", resp->gNB_ue_ngap_id, pdu_sessions_released);
itti_send_msg_to_task (TASK_NGAP, rrc->module_id, msg_p);
}
@@ -1210,47 +1190,60 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_
return -1;
}
LOG_I(NR_RRC, "[gNB %ld] gNB_ue_ngap_id %u \n", instance, gNB_ue_ngap_id);
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
LOG_I(
NR_RRC, "PDU Session Release Command: AMF_UE_NGAP_ID %lu rrc_ue_id %u release_pdusessions %d \n", cmd->amf_ue_ngap_id, gNB_ue_ngap_id, cmd->nb_pdusessions_torelease);
bool found = false;
LOG_I(NR_RRC, "PDU Session Release: AMF_UE_NGAP_ID %lu rrc_ue_id %u release_pdusessions %d \n",
cmd->amf_ue_ngap_id,
gNB_ue_ngap_id,
cmd->nb_pdusessions_torelease);
e1ap_bearer_mod_req_t req = {0};
uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
UE->xids[xid] = RRC_PDUSESSION_RELEASE;
for (int pdusession = 0; pdusession < cmd->nb_pdusessions_torelease; pdusession++) {
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, cmd->pdusession_release_params[pdusession].pdusession_id, false);
if (!pduSession) {
LOG_I(NR_RRC, "no pdusession_id, AMF requested to close it id=%d\n", cmd->pdusession_release_params[pdusession].pdusession_id);
int j=UE->nb_of_pdusessions++;
UE->pduSession[j].status = PDU_SESSION_STATUS_FAILED;
UE->pduSession[j].param.pdusession_id = cmd->pdusession_release_params[pdusession].pdusession_id;
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID};
UE->pduSession[j].cause = cause;
continue;
}
if (pduSession->status == PDU_SESSION_STATUS_FAILED) {
pduSession->xid = xid;
continue;
}
if (pduSession->status == PDU_SESSION_STATUS_ESTABLISHED) {
found = true;
LOG_I(NR_RRC, "RELEASE pdusession %d \n", pduSession->param.pdusession_id);
pduSession->status = PDU_SESSION_STATUS_TORELEASE;
pduSession->xid = xid;
pdusession_release_t *release = &cmd->pdusession_release_params[pdusession];
int session_id = release->pdusession_id;
rrc_pdusession_failed_t *fail = (rrc_pdusession_failed_t *)find_pduSession(UE->pduSessions_failed, session_id);
if (fail) {
// if failed already, no user plane: add to release list
rrc_pdusession_release_t release = {.pdusession_id = fail->pdusession_id, .xid = xid};
add_pduSession_to_release(&UE->pduSessions_to_release, UE->rrc_ue_id, release);
} else {
// search in the established PDU Sessions list
pdusession_t *pduSession = (pdusession_t *)find_pduSession(UE->pduSessions, session_id);
if (!pduSession) {
LOG_W(NR_RRC, "PDU session %d not found; add to failed list\n", session_id);
rrc_pdusession_failed_t failed = {.pdusession_id = session_id, .xid = xid};
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID};
failed.cause = cause;
add_failed_pduSession(&UE->pduSessions_failed, UE->rrc_ue_id, failed);
} else {
pduSession->xid = xid;
add_pduSession(&UE->pduSessions_to_addmod, UE->rrc_ue_id, pduSession);
LOG_I(NR_RRC, "Added item ID %d to pduSessions_to_addmod list, (total = %ld)\n",
pduSession->pdusession_id,
seq_arr_size(UE->pduSessions_to_addmod));
// Remove from setup list
rm_pduSession(UE->pduSessions, session_id);
// Fill E1AP Bearer Context Modification
pdu_session_to_remove_t *to_remove = &req.pduSessionRem[req.numPDUSessionsRem++];
to_remove->sessionId = session_id;
to_remove->cause.type = E1AP_CAUSE_RADIO_NETWORK;
to_remove->cause.value = E1AP_RADIO_CAUSE_NORMAL_RELEASE;
LOG_I(NR_RRC, "PDU session %d set to be released\n", session_id);
}
}
}
if (found) {
// TODO RRCReconfiguration To UE
LOG_I(NR_RRC, "Send RRCReconfiguration To UE \n");
if (req.numPDUSessionsRem > 0) {
if (ue_associated_to_cuup(rrc, UE)) {
req.gNB_cu_cp_ue_id = UE->rrc_ue_id;
req.gNB_cu_up_ue_id = UE->rrc_ue_id;
sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE);
rrc->cucp_cuup.bearer_context_mod(assoc_id, &req);
}
LOG_I(NR_RRC, "Send RRCReconfiguration To UE 0x%x\n", UE->rrc_ue_id);
rrc_gNB_generate_dedicatedRRCReconfiguration_release(rrc, UE, xid, cmd->nas_pdu.len, cmd->nas_pdu.buf);
} else {
// gtp tunnel delete
LOG_I(NR_RRC, "gtp tunnel delete all tunnels for UE %04x\n", UE->rnti);
gtpv1u_gnb_delete_tunnel_req_t req = {0};
req.ue_id = UE->rnti;
gtpv1u_delete_ngu_tunnel(rrc->module_id, &req);
// NGAP_PDUSESSION_RELEASE_RESPONSE
release_pduSessions(rrc, UE);
rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid);
LOG_I(NR_RRC, "Send PDU Session Release Response \n");
}

View File

@@ -74,10 +74,7 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_REQ(MessageDef *msg_p, instance_t in
int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance);
void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance,
uint32_t gNB_ue_ngap_id,
int num_pdu,
uint32_t pdu_session_id[256]);
void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, gNB_RRC_UE_t *UE);
void rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_UECapabilityInformation_t *const ue_cap_info);

View File

@@ -78,9 +78,6 @@ rrc_gNB_ue_context_t *rrc_gNB_allocate_new_ue_context(gNB_RRC_INST *rrc_instance
new_p->ue_context.rrc_ue_id = uid_linear_allocator_new(&rrc_instance_pP->uid_allocator) + 1;
rrc_gNB_ue_context_update_time(new_p);
for(int i = 0; i < NB_RB_MAX; i++)
new_p->ue_context.pduSession[i].xid = -1;
LOG_D(NR_RRC, "Returning new RRC UE context RRC ue id: %d\n", new_p->ue_context.rrc_ue_id);
return(new_p);
}

View File

@@ -64,10 +64,12 @@ static void free_ho_ctx(nr_handover_context_t *ho_ctx)
static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_drb_to_be_setup_t drbs[MAX_DRBS_PER_UE])
{
int nb_drb = 0;
for (int i = 0; i < MAX_DRBS_PER_UE; ++i) {
drb_t *rrc_drb = &ue->established_drbs[i];
if (rrc_drb->status == DRB_INACTIVE)
FOR_EACH_SEQ_ARR(drb_t *, rrc_drb, ue->drbs) {
if (nb_drb == MAX_DRBS_PER_UE) {
LOG_E(NR_RRC, "UE %d: reached max number of DRBs for F1AP, failed to setup DRB %d\n", ue->rrc_ue_id, rrc_drb->drb_id);
continue;
}
f1ap_drb_to_be_setup_t *drb = &drbs[nb_drb];
nb_drb++;
@@ -79,21 +81,19 @@ static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_
drb->up_ul_tnl_length = 1;
/* fetch an existing PDU session for this DRB */
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue, drb->drb_id);
pdusession_t *pdu = find_pduSession_from_drbId(ue, ue->pduSessions, drb->drb_id);
AssertFatal(pdu != NULL, "no PDU session for DRB ID %ld\n", drb->drb_id);
drb->nssai = pdu->param.nssai;
drb->nssai = pdu->nssai;
// for the moment, we only support one QoS flow. Put a reminder in case
// this changes
AssertFatal(pdu->param.nb_qos == 1, "only 1 Qos flow supported\n");
AssertFatal(pdu->nb_qos == 1, "only 1 Qos flow supported\n");
drb->drb_info.flows_to_be_setup_length = 1;
drb->drb_info.flows_mapped_to_drb = calloc_or_fail(1, sizeof(drb->drb_info.flows_mapped_to_drb[0]));
AssertFatal(drb->drb_info.flows_mapped_to_drb, "could not allocate memory\n");
int qfi = rrc_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[0];
int qfi = pdu->qos[0].qfi;
DevAssert(qfi > 0);
drb->drb_info.flows_mapped_to_drb[0].qfi = qfi;
pdusession_level_qos_parameter_t *in_qos_char = get_qos_characteristics(qfi, pdu);
drb->drb_info.flows_mapped_to_drb[0].qos_params.qos_characteristics = get_qos_char_from_qos_flow_param(in_qos_char);
drb->drb_info.flows_mapped_to_drb[0].qos_params.qos_characteristics = get_qos_characteristics(qfi, pdu);
/* the DRB QoS parameters: we just reuse the ones from the first flow */
drb->drb_info.drb_qos = drb->drb_info.flows_mapped_to_drb[0].qos_params;
@@ -212,19 +212,6 @@ void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, int nr_cgi, uint8_t *ho_prep_info,
}
*/
typedef struct deliver_ue_ctxt_modification_data_t {
gNB_RRC_INST *rrc;
f1ap_ue_context_modif_req_t *modification_req;
sctp_assoc_t assoc_id;
} deliver_ue_ctxt_modification_data_t;
static void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
DevAssert(deliver_pdu_data != NULL);
deliver_ue_ctxt_modification_data_t *data = deliver_pdu_data;
data->modification_req->rrc_container = (uint8_t*)buf;
data->modification_req->rrc_container_length = size;
data->rrc->mac_rrc.ue_context_modification_request(data->assoc_id, data->modification_req);
}
static void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len)
{
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);

View File

@@ -23,197 +23,266 @@
#include <stddef.h>
#include "E1AP_RLC-Mode.h"
#include "RRC/NR/nr_rrc_defs.h"
#include "T.h"
#include "asn_internal.h"
#include "assertions.h"
#include "common/platform_constants.h"
#include "common/utils/T/T.h"
#include "ngap_messages_types.h"
#include "oai_asn1.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_asn1_utils.h"
#include "common/utils/alg/find.h"
#include "openair3/ocp-gtpu/gtp_itf.h"
/** @brief Deep copy pdusession_t */
void cp_pdusession(pdusession_t *dst, const pdusession_t *src)
{
// Shallow copy
*dst = *src;
// nas_pdu
dst->nas_pdu = copy_byte_array(src->nas_pdu);
}
#ifdef ENABLE_TESTS
rrc_pdu_session_param_t *find_pduSession(gNB_RRC_UE_t *ue, int id, bool create)
{
int j;
for (j = 0; j < ue->nb_of_pdusessions; j++)
if (id == ue->pduSession[j].param.pdusession_id)
break;
if (j == ue->nb_of_pdusessions) {
if (create)
ue->nb_of_pdusessions++;
else
return NULL;
}
return ue->pduSession + j;
}
#define RRC_LOG_INFO(fmt, ...) printf("[INFO] " fmt "\n", ##__VA_ARGS__)
#define RRC_LOG_DEBUG(fmt, ...) printf("[DEBUG] " fmt "\n", ##__VA_ARGS__)
#define RRC_LOG_WARN(fmt, ...) printf("[WARN] " fmt "\n", ##__VA_ARGS__)
#define RRC_LOG_ERROR(fmt, ...) printf("[ERROR] " fmt "\n", ##__VA_ARGS__)
rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id)
#else
#include "log.h"
#include "common/utils/T/T.h"
#define RRC_LOG_INFO(fmt, ...) LOG_I(NR_RRC, fmt, ##__VA_ARGS__)
#define RRC_LOG_DEBUG(fmt, ...) LOG_D(NR_RRC, fmt, ##__VA_ARGS__)
#define RRC_LOG_WARN(fmt, ...) LOG_W(NR_RRC, fmt, ##__VA_ARGS__)
#define RRC_LOG_ERROR(fmt, ...) LOG_E(NR_RRC, fmt, ##__VA_ARGS__)
#endif
drb_t *add_rrc_drb(seq_arr_t **drb_ptr, drb_t in)
{
const drb_t *drb = &ue->established_drbs[drb_id - 1];
if (drb->status == DRB_INACTIVE) {
LOG_E(NR_RRC, "UE %d: DRB %d inactive\n", ue->rrc_ue_id, drb_id);
if (drb_ptr == NULL) {
RRC_LOG_ERROR("add_drb: Invalid input\n");
return NULL;
}
int id = drb->cnAssociation.sdap_config.pdusession_id;
return find_pduSession(ue, id, false);
SEQ_ARR_INIT(drb_ptr, drb_t, MAX_DRBS_PER_UE);
return SEQ_ARR_PUSH_BACK_AND_GET(drb_t, *drb_ptr, &in);
}
void get_pduSession_array(gNB_RRC_UE_t *ue, uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION])
static bool eq_drb_id(const void *vval, const void *vit)
{
for (int i = 0; i < ue->nb_of_pdusessions && i < NGAP_MAX_PDU_SESSION; ++i)
pdu_sessions[i] = ue->pduSession[i].param.pdusession_id;
const int *id = (const int *)vval;
const drb_t *elem = (const drb_t *)vit;
return elem->drb_id == *id;
}
drb_t *get_drb(gNB_RRC_UE_t *ue, uint8_t drb_id)
/** @brief Retrieves DRB for the input ID
* @return pointer to the found DRB, NULL if not found */
drb_t *get_drb(seq_arr_t *seq, int id)
{
DevAssert(drb_id > 0 && drb_id <= 32);
DevAssert(ue != NULL);
return &ue->established_drbs[drb_id - 1];
DevAssert(id > 0 && id <= MAX_DRBS_PER_UE);
elm_arr_t elm = find_if(seq, &id, eq_drb_id);
if (elm.found)
return (drb_t *)elm.it;
return NULL;
}
void set_default_drb_pdcp_config(struct pdcp_config_s *pdcp_config,
int do_drb_integrity,
int do_drb_ciphering,
const nr_pdcp_configuration_t *default_pdcp_config)
static bool eq_drb_pdu_session_id(const void *vval, const void *vit)
{
AssertError(pdcp_config != NULL, return, "Failed to set default PDCP configuration for DRB!\n");
pdcp_config->discardTimer = encode_discard_timer(default_pdcp_config->drb.discard_timer);
pdcp_config->pdcp_SN_SizeDL = encode_sn_size_dl(default_pdcp_config->drb.sn_size);
pdcp_config->pdcp_SN_SizeUL = encode_sn_size_ul(default_pdcp_config->drb.sn_size);
pdcp_config->t_Reordering = encode_t_reordering(default_pdcp_config->drb.t_reordering);
pdcp_config->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed;
pdcp_config->headerCompression.NotUsed = 0;
pdcp_config->integrityProtection = do_drb_integrity ? NR_PDCP_Config__drb__integrityProtection_enabled : 1;
pdcp_config->ext1.cipheringDisabled = do_drb_ciphering ? 1 : NR_PDCP_Config__ext1__cipheringDisabled_true;
const int *id = (const int *)vval;
const drb_t *elem = (const drb_t *)vit;
return elem->pdusession_id == *id;
}
void set_bearer_context_pdcp_config(bearer_context_pdcp_config_t *pdcp_config, drb_t *rrc_drb, bool um_on_default_drb)
/** @brief Finds the first DRB with the given PDU session ID.
* @return Pointer to matching drb_t or NULL if not found. */
drb_t *find_drb(seq_arr_t *seq, int pdusession_id)
{
AssertError(rrc_drb != NULL && pdcp_config != NULL, return, "Failed to set default bearer context PDCP configuration!\n");
pdcp_config->pDCP_SN_Size_UL = rrc_drb->pdcp_config.pdcp_SN_SizeUL;
pdcp_config->pDCP_SN_Size_DL = rrc_drb->pdcp_config.pdcp_SN_SizeDL;
pdcp_config->discardTimer = rrc_drb->pdcp_config.discardTimer;
pdcp_config->reorderingTimer = rrc_drb->pdcp_config.t_Reordering;
pdcp_config->rLC_Mode = um_on_default_drb ? E1AP_RLC_Mode_rlc_um_bidirectional : E1AP_RLC_Mode_rlc_am;
elm_arr_t elm = find_if(seq, &pdusession_id, eq_drb_pdu_session_id);
if (elm.found)
return (drb_t *)elm.it;
return NULL;
}
drb_t *generateDRB(gNB_RRC_UE_t *ue,
uint8_t drb_id,
const rrc_pdu_session_param_t *pduSession,
bool enable_sdap,
int do_drb_integrity,
int do_drb_ciphering,
const nr_pdcp_configuration_t *pdcp_config)
/** @brief Removes all DRBs associated with a given PDU session ID */
void remove_drbs_by_pdu_session(seq_arr_t **drbs, int pdusession_id)
{
DevAssert(ue != NULL);
if (drbs == NULL)
return;
LOG_I(NR_RRC, "UE %d: configure DRB ID %d for PDU session ID %d\n", ue->rrc_ue_id, drb_id, pduSession->param.pdusession_id);
drb_t *drb;
while ((drb = find_drb(*drbs, pdusession_id)) != NULL) {
RRC_LOG_INFO("Removing DRB ID %d associated with PDU Session ID %d\n", drb->drb_id, pdusession_id);
seq_arr_erase(*drbs, drb);
}
drb_t *est_drb = &ue->established_drbs[drb_id - 1];
DevAssert(est_drb->status == DRB_INACTIVE);
if (!seq_arr_size(*drbs)) {
SEQ_ARR_CLEANUP_AND_FREE(*drbs, NULL);
}
}
est_drb->status = DRB_ACTIVE;
est_drb->drb_id = drb_id;
est_drb->cnAssociation.sdap_config.defaultDRB = true;
static bool eq_pdu_session_id(const void *vval, const void *vit)
{
const int *id = (const int *)vval;
const pdusession_t *elem = (const pdusession_t *)vit;
return elem->pdusession_id == *id;
}
/* SDAP Configuration */
est_drb->cnAssociation.present = NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config;
est_drb->cnAssociation.sdap_config.pdusession_id = pduSession->param.pdusession_id;
if (enable_sdap) {
est_drb->cnAssociation.sdap_config.sdap_HeaderDL = NR_SDAP_Config__sdap_HeaderDL_present;
est_drb->cnAssociation.sdap_config.sdap_HeaderUL = NR_SDAP_Config__sdap_HeaderUL_present;
/** @brief Free pdusession_t */
void free_pdusession(void *ptr)
{
// nothing to free
}
/** @brief Retrieves PDU Session for the input ID
* @return pointer to the found PDU Session, NULL if not found */
void *find_pduSession(seq_arr_t *seq, int id)
{
elm_arr_t elm = find_if(seq, &id, eq_pdu_session_id);
if (elm.found)
return elm.it;
return NULL;
}
/** @brief Adds a new PDU Session type pdusession_t to the list (either setup or addmod)
* @return pointer to the new PDU Session (or if already existing, to the found one */
pdusession_t *add_pduSession(seq_arr_t **sessions_ptr, const int rrc_ue_id, pdusession_t *in)
{
if (sessions_ptr == NULL || in == NULL) {
RRC_LOG_ERROR("add_pduSession: Invalid input\n");
return NULL;
}
// Initialize seq_arr if necessary
SEQ_ARR_INIT(sessions_ptr, pdusession_t, NGAP_MAX_PDU_SESSION);
pdusession_t *found = (pdusession_t *)find_pduSession(*sessions_ptr, in->pdusession_id);
if (found) {
RRC_LOG_WARN("PDU Session already in the list!\n");
return found;
}
// Add item to the list
pdusession_t *added = SEQ_ARR_PUSH_BACK_AND_GET(pdusession_t, *sessions_ptr, in);
return added;
}
/** @brief Update an established PDU Session (setup list) after a "modify" procedure */
bool update_pduSession(seq_arr_t **sessions_ptr, const pdusession_t *mod)
{
if (sessions_ptr == NULL || mod == NULL) {
RRC_LOG_ERROR("update_pduSession: Invalid input\n");
return false;
}
pdusession_t *found = (pdusession_t *)find_pduSession(*sessions_ptr, mod->pdusession_id);
if (!found) {
RRC_LOG_ERROR("PDU Session not found in the setup list (UE->pduSessions)\n");
return false;
}
// Update the setup PDU Session with new status
*found = *mod; // Shallow copy
return true;
}
/** @brief Adds a new PDU Session to the list
* @return pointer to the new PDU Session */
rrc_pdusession_release_t *add_pduSession_to_release(seq_arr_t **sessions_ptr, const int rrc_ue_id, rrc_pdusession_release_t in)
{
if (sessions_ptr == NULL) {
RRC_LOG_ERROR("add_pduSession_to_release: Invalid input\n");
return NULL;
}
SEQ_ARR_INIT(sessions_ptr, rrc_pdusession_release_t, NGAP_MAX_PDU_SESSION);
rrc_pdusession_release_t *added = SEQ_ARR_PUSH_BACK_AND_GET(rrc_pdusession_release_t, *sessions_ptr, &in);
RRC_LOG_INFO("Added PDU Session %d to release (total = %ld)\n", added->pdusession_id, seq_arr_size(*sessions_ptr));
return added;
}
/** @brief Adds a new PDU Session to the list
* @return pointer to the new PDU Session */
rrc_pdusession_failed_t *add_failed_pduSession(seq_arr_t **sessions_ptr, const int rrc_ue_id, rrc_pdusession_failed_t in)
{
if (sessions_ptr == NULL) {
RRC_LOG_ERROR("add_failed_pduSession: Invalid input\n");
return NULL;
}
SEQ_ARR_INIT(sessions_ptr, rrc_pdusession_failed_t, NGAP_MAX_PDU_SESSION);
rrc_pdusession_failed_t *added = SEQ_ARR_PUSH_BACK_AND_GET(rrc_pdusession_failed_t, *sessions_ptr, &in);
RRC_LOG_INFO("Added failed PDU Session %d (total = %ld)\n", added->pdusession_id, seq_arr_size(*sessions_ptr));
return added;
}
pdusession_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, seq_arr_t *seq, int drb_id)
{
const drb_t *drb = get_drb(ue->drbs, drb_id);
if (!drb) {
RRC_LOG_ERROR("UE %d: DRB %d not found\n", ue->rrc_ue_id, drb_id);
return NULL;
}
int id = drb->pdusession_id;
return (pdusession_t *)find_pduSession(seq, id);
}
/** @brief Delete all N2 GTP tunnels for PDU Session to release
* Context: CU/CU-CP, so deletes NG-C (N2) tunnels only */
void release_pduSessions(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue)
{
// GTP tunnel cleanup
gtpv1u_gnb_delete_tunnel_req_t req = {0};
req.ue_id = ue->rnti;
FOR_EACH_SEQ_ARR(rrc_pdusession_release_t *, release, ue->pduSessions_to_release) {
RRC_LOG_INFO("Delete GTP tunnels for UE %04x, PDU Session ID %d\n", ue->rnti, release->pdusession_id);
req.pdusession_id[req.num_pdusession++] = release->pdusession_id;
}
gtpv1u_delete_ngu_tunnel(rrc->module_id, &req);
}
/** @brief Removes the PDU Session with the given ID from the list.
* @return true if a session was removed, false if not found. */
bool rm_pduSession(seq_arr_t *seq, int pdusession_id)
{
if (seq == NULL) {
RRC_LOG_ERROR("rm_pduSession: seq is NULL\n");
return false;
}
elm_arr_t elm = find_if(seq, &pdusession_id, eq_pdu_session_id);
if (!elm.found) {
RRC_LOG_WARN("rm_pduSession: PDU Session %d not found\n", pdusession_id);
return false;
}
seq_arr_erase(seq, elm.it); // shallow erase
RRC_LOG_INFO("Removed PDU Session %d, remaining = %ld\n", pdusession_id, seq_arr_size(seq));
return true;
}
/** @brief Find active PDU Session for @param pdusession_id */
pdusession_t *find_active_pdu_session(gNB_RRC_UE_t *ue_p, int pdusession_id)
{
pdusession_t *pdu = find_pduSession(ue_p->pduSessions_to_addmod, pdusession_id);
if (pdu) {
LOG_I(NR_RRC, "UE %d: found addmod PDU session %d\n", ue_p->rrc_ue_id, pdusession_id);
return pdu;
} else {
est_drb->cnAssociation.sdap_config.sdap_HeaderDL = NR_SDAP_Config__sdap_HeaderDL_absent;
est_drb->cnAssociation.sdap_config.sdap_HeaderUL = NR_SDAP_Config__sdap_HeaderUL_absent;
pdu = find_pduSession(ue_p->pduSessions, pdusession_id);
LOG_I(NR_RRC, "UE %d: found setup PDU session %d\n", ue_p->rrc_ue_id, pdusession_id);
return pdu;
}
for (int qos_flow_index = 0; qos_flow_index < pduSession->param.nb_qos; qos_flow_index++) {
est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index] = pduSession->param.qos[qos_flow_index].qfi;
if (pduSession->param.qos[qos_flow_index].fiveQI > 5)
est_drb->status = DRB_ACTIVE_NONGBR;
else
est_drb->status = DRB_ACTIVE;
}
/* PDCP Configuration */
set_default_drb_pdcp_config(&est_drb->pdcp_config, do_drb_integrity, do_drb_ciphering, pdcp_config);
drb_t *rrc_drb = get_drb(ue, drb_id);
DevAssert(rrc_drb == est_drb); /* to double check that we create the same which we would retrieve */
return rrc_drb;
return NULL;
}
NR_DRB_ToAddMod_t *generateDRB_ASN1(const drb_t *drb_asn1)
bearer_context_pdcp_config_t set_bearer_context_pdcp_config(const nr_pdcp_configuration_t pdcp, bool um_on_default_drb)
{
NR_DRB_ToAddMod_t *DRB_config = CALLOC(1, sizeof(*DRB_config));
NR_SDAP_Config_t *SDAP_config = CALLOC(1, sizeof(NR_SDAP_Config_t));
asn1cCalloc(DRB_config->cnAssociation, association);
asn1cCalloc(SDAP_config->mappedQoS_FlowsToAdd, sdapFlows);
asn1cCalloc(DRB_config->pdcp_Config, pdcpConfig);
asn1cCalloc(pdcpConfig->drb, drb);
DRB_config->drb_Identity = drb_asn1->drb_id;
association->present = drb_asn1->cnAssociation.present;
/* SDAP Configuration */
SDAP_config->pdu_Session = drb_asn1->cnAssociation.sdap_config.pdusession_id;
SDAP_config->sdap_HeaderDL = drb_asn1->cnAssociation.sdap_config.sdap_HeaderDL;
SDAP_config->sdap_HeaderUL = drb_asn1->cnAssociation.sdap_config.sdap_HeaderUL;
SDAP_config->defaultDRB = drb_asn1->cnAssociation.sdap_config.defaultDRB;
for (int qos_flow_index = 0; qos_flow_index < QOSFLOW_MAX_VALUE; qos_flow_index++) {
if (drb_asn1->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index] != 0) {
asn1cSequenceAdd(sdapFlows->list, NR_QFI_t, qfi);
*qfi = drb_asn1->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index];
}
}
association->choice.sdap_Config = SDAP_config;
/* PDCP Configuration */
asn1cCallocOne(drb->discardTimer, drb_asn1->pdcp_config.discardTimer);
asn1cCallocOne(drb->pdcp_SN_SizeUL, drb_asn1->pdcp_config.pdcp_SN_SizeUL);
asn1cCallocOne(drb->pdcp_SN_SizeDL, drb_asn1->pdcp_config.pdcp_SN_SizeDL);
asn1cCallocOne(pdcpConfig->t_Reordering, drb_asn1->pdcp_config.t_Reordering);
drb->headerCompression.present = drb_asn1->pdcp_config.headerCompression.present;
drb->headerCompression.choice.notUsed = drb_asn1->pdcp_config.headerCompression.NotUsed;
if (!drb_asn1->pdcp_config.integrityProtection) {
asn1cCallocOne(drb->integrityProtection, drb_asn1->pdcp_config.integrityProtection);
}
if (!drb_asn1->pdcp_config.ext1.cipheringDisabled) {
asn1cCalloc(pdcpConfig->ext1, ext1);
asn1cCallocOne(ext1->cipheringDisabled, drb_asn1->pdcp_config.ext1.cipheringDisabled);
}
return DRB_config;
}
uint8_t get_next_available_drb_id(gNB_RRC_UE_t *ue)
{
for (uint8_t drb_id = 0; drb_id < MAX_DRBS_PER_UE; drb_id++)
if (ue->established_drbs[drb_id].status == DRB_INACTIVE)
return drb_id + 1;
/* From this point, we need to handle the case that all DRBs are already used by the UE. */
LOG_E(RRC, "Error - All the DRBs are used - Handle this\n");
return DRB_INACTIVE;
}
int get_number_active_drbs(gNB_RRC_UE_t *ue)
{
int n = 0;
for (int i = 0; i < MAX_DRBS_PER_UE; ++i)
if (ue->established_drbs[i].status != DRB_INACTIVE)
n++;
return n;
bearer_context_pdcp_config_t out = {0};
out.pDCP_SN_Size_UL = encode_sn_size_ul(pdcp.drb.sn_size);
out.pDCP_SN_Size_DL = encode_sn_size_dl(pdcp.drb.sn_size);
out.discardTimer = encode_discard_timer(pdcp.drb.discard_timer);
out.reorderingTimer = encode_t_reordering(pdcp.drb.t_reordering);
out.rLC_Mode = um_on_default_drb ? E1AP_RLC_Mode_rlc_um_bidirectional : E1AP_RLC_Mode_rlc_am;
out.pDCP_Reestablishment = false;
return out;
}

View File

@@ -24,61 +24,46 @@
#include <stdbool.h>
#include <stdint.h>
#include "NR_DRB-ToAddMod.h"
#include "e1ap_messages_types.h"
#include "nr_rrc_defs.h"
#define DRB_ACTIVE_NONGBR (2) /* DRB is used for Non-GBR Flows */
#define DRB_ACTIVE (1)
#define DRB_INACTIVE (0)
#define GBR_FLOW (1)
#define NONGBR_FLOW (0)
/// @brief Generates an ASN1 DRB-ToAddMod, from the established_drbs in gNB_RRC_UE_t.
/// @param drb_t drb_asn1
/// @return Returns the ASN1 DRB-ToAddMod structs.
NR_DRB_ToAddMod_t *generateDRB_ASN1(const drb_t *drb_asn1);
/// @brief retrieve the data structure representing DRB with ID drb_id of UE ue
drb_t *get_drb(gNB_RRC_UE_t *ue, uint8_t drb_id);
drb_t *get_drb(seq_arr_t *seq, int id);
/// @brief Creates and stores a DRB in the gNB_RRC_UE_t struct
/// @param ue The gNB_RRC_UE_t struct that holds information for the UEs
/// @param drb_id The Data Radio Bearer Identity to be created for the established DRB.
/// @param pduSession The PDU Session that the DRB is created for.
/// @param enable_sdap If true the SDAP header will be added to the packet, else it will not add or search for SDAP header.
/// @param do_drb_integrity
/// @param do_drb_ciphering
/// @param pdcp_config
/// @return returns a pointer to the generated DRB structure
drb_t *generateDRB(gNB_RRC_UE_t *ue,
uint8_t drb_id,
const rrc_pdu_session_param_t *pduSession,
bool enable_sdap,
int do_drb_integrity,
int do_drb_ciphering,
const nr_pdcp_configuration_t *pdcp_config);
/// @brief retrieve PDU session of UE ue with ID id
void *find_pduSession(seq_arr_t *seq, int id);
/// @brief return the next available (inactive) DRB ID of UE ue
uint8_t get_next_available_drb_id(gNB_RRC_UE_t *ue);
/// @brief Add a new PDU session @param in to the list @param for sessions_ptr for UE @param rrc_ue_id
pdusession_t *add_pduSession(seq_arr_t **sessions_ptr, const int rrc_ue_id, pdusession_t *in);
/// @brief returns the number of active DRBs for this UE
int get_number_active_drbs(gNB_RRC_UE_t *ue);
/// @brief retrieve PDU session of UE ue with ID id, optionally creating it if
/// create is true
rrc_pdu_session_param_t *find_pduSession(gNB_RRC_UE_t *ue, int id, bool create);
/// @brief Update an established PDU Session in @param sessions_ptr (setup list) with modified @param mod session
bool update_pduSession(seq_arr_t **sessions_ptr, const pdusession_t *mod);
/// @brief get PDU session of UE ue through the DRB drb_id
rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id);
pdusession_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, seq_arr_t *seq, int drb_id);
/// @brief get the PDU sessions of this UE in a single array
void get_pduSession_array(gNB_RRC_UE_t *ue, uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION]);
rrc_pdusession_failed_t *add_failed_pduSession(seq_arr_t **sessions_ptr, const int rrc_ue_id, rrc_pdusession_failed_t in);
rrc_pdusession_release_t *add_pduSession_to_release(seq_arr_t **sessions_ptr, const int rrc_ue_id, rrc_pdusession_release_t in);
/// @brief set PDCP configuration in a bearer context management message
void set_bearer_context_pdcp_config(bearer_context_pdcp_config_t *pdcp_config, drb_t *rrc_drb, bool um_on_default_drb);
bearer_context_pdcp_config_t set_bearer_context_pdcp_config(const nr_pdcp_configuration_t pdcp, bool um_on_default_drb);
/// @brief Deep copy an instance of struct pdusession_t
void cp_pdusession(pdusession_t *dst, const pdusession_t *src);
void free_pdusession(void *ptr);
bool rm_pduSession(seq_arr_t *seq, int pdusession_id);
drb_t *add_rrc_drb(seq_arr_t **drb_ptr, drb_t in);
void release_pduSessions(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
void remove_drbs_by_pdu_session(seq_arr_t **drbs, int pdusession_id);
drb_t *find_drb(seq_arr_t *seq, int pdusession_id);
pdusession_t *find_active_pdu_session(gNB_RRC_UE_t *ue_p, int pdusession_id);
#endif

View File

@@ -0,0 +1,16 @@
add_executable(rrc_bearers_test rrc_bearers_test.c ../rrc_gNB_radio_bearers.c ${OPENAIR2_DIR}/LAYER2/nr_pdcp/asn1_utils.c)
add_dependencies(tests rrc_bearers_test)
add_test(NAME rrc_bearers_test COMMAND rrc_bearers_test)
target_link_libraries(rrc_bearers_test PRIVATE ds T alg GTPV1U ITTI)
target_include_directories(rrc_bearers_test PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/common/utils/ds
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/openair2/E1AP/MESSAGES
${CMAKE_BINARY_DIR}/openair2/RRC/LTE/MESSAGES
${CMAKE_BINARY_DIR}/openair2/RRC/NR/MESSAGES)
if(ENABLE_TESTS)
target_compile_definitions(rrc_bearers_test PRIVATE ENABLE_TESTS)
endif()

View File

@@ -0,0 +1,246 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "common/utils/utils.h"
#include "common/utils/assertions.h"
#include "openair2/RRC/NR/rrc_gNB_radio_bearers.h"
#include "ds/seq_arr.h"
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
{
printf("detected error at %s:%d:%s: %s\n", file, line, function, s);
abort();
}
int nr_rlc_get_available_tx_space(int module_id, int rnti, int drb_id) { return 0; }
softmodem_params_t *get_softmodem_params(void) { return NULL; }
configmodule_interface_t *uniqCfg = NULL;
static void test_add_and_find_pduSession(void)
{
gNB_RRC_UE_t *ue = calloc_or_fail(1, sizeof(*ue));
ue->rrc_ue_id = 0x4321;
pdusession_t input = {0};
input.pdusession_id = 5;
input.n3_incoming.teid = 0x1001;
pdusession_t *session = add_pduSession(&ue->pduSessions_to_addmod, ue->rrc_ue_id, &input);
AssertFatal(session != NULL, "Could not add PDU Session\n");
AssertFatal(session->pdusession_id == input.pdusession_id, "PDU Session ID mismatch in added PDU Session\n");
AssertFatal(session->n3_incoming.teid == input.n3_incoming.teid, "teid mismatch in added PDU Session\n");
pdusession_t *found = find_pduSession(ue->pduSessions_to_addmod, input.pdusession_id);
AssertFatal(found != NULL, "Could not find PDU Session\n");
AssertFatal(found == session, "Found PDU Session mismatch\n");
SEQ_ARR_CLEANUP_AND_FREE(ue->pduSessions_to_addmod, NULL);
free(ue);
}
static void test_duplicate_add_pduSession(void)
{
gNB_RRC_UE_t ue = {0};
ue.rrc_ue_id = 0x4444;
pdusession_t input1 = {0};
input1.pdusession_id = 3;
input1.n3_incoming.teid = 2002;
pdusession_t input2 = {0};
input2.pdusession_id = 3;
input2.n3_incoming.teid = 9999;
pdusession_t *s1 = add_pduSession(&ue.pduSessions_to_addmod, ue.rrc_ue_id, &input1);
AssertFatal(s1 != NULL, "First add_pduSession failed\\n");
pdusession_t *s2 = add_pduSession(&ue.pduSessions_to_addmod, ue.rrc_ue_id, &input2);
AssertFatal(s2 == s1, "Duplicate add_pduSession returned different pointer\\n");
AssertFatal(s2->n3_incoming.teid == input1.n3_incoming.teid, "Original TEID should be retained\\n");
SEQ_ARR_CLEANUP_AND_FREE(ue.pduSessions_to_addmod, NULL);
}
static void test_rm_pduSession(void)
{
gNB_RRC_UE_t ue = {0};
ue.rrc_ue_id = 0x1234;
pdusession_t input = {0};
input.pdusession_id = 4;
input.n3_incoming.teid = 777;
pdusession_t *s = add_pduSession(&ue.pduSessions_to_addmod, ue.rrc_ue_id, &input);
AssertFatal(s != NULL, "add_pduSession failed\\n");
rm_pduSession(ue.pduSessions_to_addmod, input.pdusession_id);
AssertFatal(find_pduSession(ue.pduSessions_to_addmod, input.pdusession_id) == NULL, "PDU Session was not removed\\n");
SEQ_ARR_CLEANUP_AND_FREE(ue.pduSessions_to_addmod, NULL);
}
static void test_update_pduSession(void)
{
gNB_RRC_UE_t ue = {0};
pdusession_t in = {.pdusession_id = 1, .n3_incoming.teid = 111};
add_pduSession(&ue.pduSessions_to_addmod, ue.rrc_ue_id, &in);
pdusession_t *orig = find_pduSession(ue.pduSessions_to_addmod, 1);
AssertFatal(orig != NULL, "Original PDU session not found before update");
AssertFatal(orig->n3_incoming.teid == 111, "Original TEID mismatch");
pdusession_t updated = {.pdusession_id = 1, .n3_incoming.teid = 999};
bool ok = update_pduSession(&ue.pduSessions_to_addmod, &updated);
AssertFatal(ok, "update_pduSession failed");
pdusession_t *found = find_pduSession(ue.pduSessions_to_addmod, 1);
AssertFatal(found && found->n3_incoming.teid == 999, "TEID not updated");
SEQ_ARR_CLEANUP_AND_FREE(ue.pduSessions_to_addmod, NULL);
}
static void test_add_pduSession_to_release(void)
{
gNB_RRC_UE_t ue = {0};
rrc_pdusession_release_t rel = {.pdusession_id = 20};
rrc_pdusession_release_t *r = add_pduSession_to_release(&ue.pduSessions_to_release, ue.rrc_ue_id, rel);
AssertFatal(r != NULL && r->pdusession_id == 20, "add_pduSession_to_release failed");
SEQ_ARR_CLEANUP_AND_FREE(ue.pduSessions_to_release, NULL);
}
static void test_find_pduSession_from_drbId(void)
{
gNB_RRC_UE_t ue = {0};
drb_t drb = {0};
drb.drb_id = 3;
drb.pdusession_id = 66;
add_rrc_drb(&ue.drbs, drb);
AssertFatal(ue.drbs != NULL, "Failed to add DRB");
pdusession_t in = {0};
in.pdusession_id = 66;
add_pduSession(&ue.pduSessions_to_addmod, ue.rrc_ue_id, &in);
pdusession_t *found = find_pduSession_from_drbId(&ue, ue.pduSessions_to_addmod, 3);
AssertFatal(found && found->pdusession_id == 66, "find_pduSession_from_drbId failed");
SEQ_ARR_CLEANUP_AND_FREE(ue.pduSessions_to_addmod, NULL);
SEQ_ARR_CLEANUP_AND_FREE(ue.drbs, NULL);
}
// ---------------- DRB TESTS ----------------
static void test_add_rrc_drb(void)
{
seq_arr_t *drbs = NULL;
drb_t in = {0};
in.drb_id = 7;
in.pdusession_id = 70;
drb_t *added = add_rrc_drb(&drbs, in);
AssertFatal(added && added->drb_id == 7, "add_rrc_drb failed");
drb_t *found = find_drb(drbs, 70);
AssertFatal(found && found == added, "find_drb failed");
SEQ_ARR_CLEANUP_AND_FREE(drbs, NULL);
}
static void test_get_drb(void)
{
seq_arr_t *drbs = NULL;
drb_t in = {.drb_id = 5};
drb_t *added = add_rrc_drb(&drbs, in);
AssertFatal(added && added->drb_id == 5, "add_rrc_drb failed");
drb_t *got = get_drb(drbs, 5);
AssertFatal(got && got->drb_id == 5, "get_drb failed");
SEQ_ARR_CLEANUP_AND_FREE(drbs, NULL);
}
static void test_find_drb(void)
{
seq_arr_t *drbs = NULL;
drb_t in = {.drb_id = 2, .pdusession_id = 88};
drb_t *added = add_rrc_drb(&drbs, in);
AssertFatal(added && added->pdusession_id == 88, "add_rrc_drb failed");
drb_t *found = find_drb(drbs, 88);
AssertFatal(found && found->drb_id == 2, "find_drb failed");
SEQ_ARR_CLEANUP_AND_FREE(drbs, NULL);
}
static void test_remove_drbs_by_pdu_session(void)
{
seq_arr_t *drbs = NULL;
drb_t in1 = {.drb_id = 1, .pdusession_id = 10};
drb_t in2 = {.drb_id = 2, .pdusession_id = 10};
drb_t in3 = {.drb_id = 3, .pdusession_id = 20};
drb_t *add1 = add_rrc_drb(&drbs, in1);
AssertFatal(add1 && add1->pdusession_id == 10, "add_rrc_drb failed");
drb_t *add2 = add_rrc_drb(&drbs, in2);
AssertFatal(add2 && add2->drb_id == 2, "add_rrc_drb failed");
drb_t *add3 = add_rrc_drb(&drbs, in3);
AssertFatal(add3 && add3->drb_id == 3, "add_rrc_drb failed");
remove_drbs_by_pdu_session(&drbs, 20); // Remove DRB 3
AssertFatal(seq_arr_size(drbs) == 2, "unexpected size of drbs seq_arr");
remove_drbs_by_pdu_session(&drbs, 10); // Remove all remaining DRBs
AssertFatal(drbs == NULL, "remove_drbs_by_pdu_session failed");
SEQ_ARR_CLEANUP_AND_FREE(drbs, NULL);
}
int main()
{
// PDU Session
test_add_and_find_pduSession();
test_duplicate_add_pduSession();
test_rm_pduSession();
test_update_pduSession();
test_add_pduSession_to_release();
test_find_pduSession_from_drbId();
// DRB
test_add_rrc_drb();
test_get_drb();
test_find_drb();
test_remove_drbs_by_pdu_session();
return 0;
}

View File

@@ -46,7 +46,6 @@
#include "uper_encoder.h"
#include "uper_decoder.h"
#include "rrc_defs.h"
#include "rrc_proto.h"
#include "L2_interface_ue.h"
#include "LAYER2/NR_MAC_UE/mac_proto.h"

View File

@@ -577,12 +577,11 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id)
{
nr_sdap_entity_t *entityPtr = sdap_info.sdap_entity_llist;
nr_sdap_entity_t *entityPrev = NULL;
bool ret = false;
int upperBound = 0;
if (entityPtr == NULL && (pdusession_id) * (pdusession_id - NGAP_MAX_PDU_SESSION) > 0) {
LOG_E(SDAP, "SDAP entities not established or Invalid range of pdusession_id [0, 256].\n");
return ret;
return false;
}
LOG_D(SDAP, "Deleting SDAP entity for UE %lx and PDU Session id %d\n", ue_id, entityPtr->pdusession_id);
@@ -592,7 +591,7 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id)
remove_ip_if(entityPtr);
free(entityPtr);
LOG_D(SDAP, "Successfully deleted Entity.\n");
ret = true;
return true;
} else {
while ((entityPtr->ue_id != ue_id || entityPtr->pdusession_id != pdusession_id) && entityPtr->next_entity != NULL
&& upperBound < SDAP_MAX_NUM_OF_ENTITIES) {
@@ -608,11 +607,11 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id)
}
free(entityPtr);
LOG_D(SDAP, "Successfully deleted Entity.\n");
ret = true;
return false;
}
}
LOG_E(SDAP, "Entity does not exist or it was not found.\n");
return ret;
return false;
}
bool nr_sdap_delete_ue_entities(ue_id_t ue_id)

View File

@@ -706,9 +706,10 @@ static void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const O
return decoded;
}
static pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params)
static qos_flow_setup_request_item_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params)
{
pdusession_level_qos_parameter_t out = {0};
qos_flow_setup_request_item_t out = {0};
qos_characteristics_t *qos_char = &out.qos_params.qos_characteristics;
// QFI
out.qfi = qfi;
AssertFatal(params != NULL, "QoS parameters are NULL\n");
@@ -717,20 +718,20 @@ static pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlow
AssertFatal(qosChar != NULL, "QoS characteristics are NULL\n");
if (qosChar->present == NGAP_QosCharacteristics_PR_nonDynamic5QI) {
AssertFatal(qosChar->choice.nonDynamic5QI != NULL, "nonDynamic5QI is NULL\n");
out.fiveQI_type = NON_DYNAMIC;
out.fiveQI = qosChar->choice.nonDynamic5QI->fiveQI;
qos_char->qos_type = NON_DYNAMIC;
qos_char->non_dynamic.fiveqi = qosChar->choice.nonDynamic5QI->fiveQI;
} else if (qosChar->present == NGAP_QosCharacteristics_PR_dynamic5QI) {
AssertFatal(qosChar->choice.dynamic5QI != NULL, "dynamic5QI is NULL\n");
out.fiveQI_type = DYNAMIC;
out.fiveQI = *qosChar->choice.dynamic5QI->fiveQI;
qos_char->qos_type = DYNAMIC;
qos_char->dynamic.fiveqi = *qosChar->choice.dynamic5QI->fiveQI;
} else {
AssertFatal(0, "Unsupported QoS Characteristics present value: %d\n", qosChar->present);
}
// Allocation and Retention Priority
const NGAP_AllocationAndRetentionPriority_t *arp = &params->allocationAndRetentionPriority;
out.allocation_retention_priority.priority_level = arp->priorityLevelARP;
out.allocation_retention_priority.pre_emp_capability = arp->pre_emptionCapability;
out.allocation_retention_priority.pre_emp_vulnerability = arp->pre_emptionVulnerability;
out.qos_params.arp.priority_level = arp->priorityLevelARP;
out.qos_params.arp.preemption_capability = arp->pre_emptionCapability;
out.qos_params.arp.preemption_vulnerability = arp->pre_emptionVulnerability;
return out;
}