mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-17 22:50:31 +00:00
Compare commits
27 Commits
pre-commit
...
refactor-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e655a4ff2d | ||
|
|
d94d2e3311 | ||
|
|
5802bc61c1 | ||
|
|
0c1f33a664 | ||
|
|
252f93a52b | ||
|
|
2185c72adf | ||
|
|
1caeb4e525 | ||
|
|
5ad8a14651 | ||
|
|
e20a225172 | ||
|
|
d61877b7e1 | ||
|
|
ef270b47ce | ||
|
|
700171fbef | ||
|
|
14401f81b8 | ||
|
|
48a608494b | ||
|
|
a5df4b4892 | ||
|
|
edc19c6110 | ||
|
|
5597f8281e | ||
|
|
c0f284319a | ||
|
|
39e670286a | ||
|
|
18279503d0 | ||
|
|
a18e2961f9 | ||
|
|
e74a071958 | ||
|
|
1ba413cd2d | ||
|
|
e939d0c9b0 | ||
|
|
3bd14be930 | ||
|
|
86ce851b9f | ||
|
|
0f7aed20fe |
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
```
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
add_subdirectory(MESSAGES)
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(¶ms);
|
||||
@@ -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 AMF–UE 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: ");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
16
openair2/RRC/NR/tests/CMakeLists.txt
Normal file
16
openair2/RRC/NR/tests/CMakeLists.txt
Normal 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()
|
||||
246
openair2/RRC/NR/tests/rrc_bearers_test.c
Normal file
246
openair2/RRC/NR/tests/rrc_bearers_test.c
Normal 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;
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = ¶ms->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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user