Merge branch 'next' into agpl_next

# Conflicts:
#	srsue/src/set_net_admin_caps.cc
master
Codebot 2 years ago committed by SRS codebot
commit 358070fe30

@ -107,12 +107,14 @@ option(ENABLE_ALL_TEST "Enable all unit/component test" OFF)
# (gcc-ar, gcc-nm, ...).
option(BUILD_WITH_LTO "Enable LTO (experimental)" OFF)
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64")
set(GCC_ARCH armv8-a CACHE STRING "GCC compile for specific architecture.")
message(STATUS "Detected aarch64 processor")
else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64")
set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.")
endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64")
if(NOT GCC_ARCH)
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64")
set(GCC_ARCH armv8-a CACHE STRING "GCC compile for specific architecture.")
message(STATUS "Detected aarch64 processor")
else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64")
set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.")
endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64")
endif()
# On RAM constrained (embedded) systems it may be useful to limit parallel compilation with, e.g. -DPARALLEL_COMPILE_JOBS=1
if (PARALLEL_COMPILE_JOBS)

@ -44,6 +44,9 @@
#define HAVE_NPDSCH 1
#define NPDCCH_SF_IDX 1
#define NOF_TX_ANT 1
#define MAX_SRATE_DELTA 2 // allowable delta (in Hz) between requested and actual sample rate
static const uint8_t dummy_sib1_payload[] = {0x43, 0x4d, 0xd0, 0x92, 0x22, 0x06, 0x04, 0x30, 0x28,
0x6e, 0x87, 0xd0, 0x4b, 0x13, 0x90, 0xb4, 0x12, 0xa1,
@ -74,6 +77,7 @@ static uint32_t i_rep_val = 0;
static char* rf_args = "";
static float rf_amp = 0.8, rf_gain = 70.0, rf_freq = 0;
static float file_snr = -100.0;
static char* rf_dev = "";
static bool null_file_sink = false;
static srsran_random_t* random_gen;
@ -92,22 +96,23 @@ static srsran_ra_nbiot_dl_dci_t ra_dl;
static srsran_ra_nbiot_dl_dci_t ra_dl_sib1;
static srsran_chest_dl_nbiot_t ch_est;
static srsran_mib_nb_t mib_nb;
static uint32_t sched_info_tag =
0; // according to Table 16.4.1.3-3 in 36.213, 0 means 4 NPDSCH repetitions with TBS 208
static uint32_t sched_info_tag = 0;
// according to Table 16.4.1.3-3 in 36.213, 0 means 4 NPDSCH repetitions with TBS 208
static cf_t *sf_buffer = NULL, *output_buffer = NULL;
static int sf_n_re = 0, sf_n_samples = 0;
void usage(char* prog)
{
printf("Usage: %s [agmiftlReosncvrpu]\n", prog);
printf("Usage: %s [aeOgfostmirnlRpv]\n", prog);
#ifndef DISABLE_RF
printf("\t-a RF args [Default %s]\n", rf_args);
printf("\t-e RF amplitude [Default %.2f]\n", rf_amp);
printf("\t-O RF device [Default use RF board]\n");
printf("\t-g RF TX gain [Default %.2f dB]\n", rf_gain);
printf("\t-f RF TX frequency [Default %.1f MHz]\n", rf_freq / 1000000);
#else
printf("\t RF is disabled.\n");
printf("\t RF is disabled!\n");
#endif
printf("\t-o output_file [Default use RF board]\n");
printf("\t-s SNR-10 (only if output to file) [Default %f]\n", file_snr);
@ -125,7 +130,7 @@ void usage(char* prog)
void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "aglfmiosncrtvpuR")) != -1) {
while ((opt = getopt(argc, argv, "aeOgfostmirnlRpv")) != -1) {
switch (opt) {
case 'a':
rf_args = argv[optind];
@ -136,6 +141,9 @@ void parse_args(int argc, char** argv)
case 'e':
rf_amp = strtof(argv[optind], NULL);
break;
case 'O':
rf_dev = argv[optind];
break;
case 'f':
rf_freq = strtof(argv[optind], NULL);
break;
@ -148,12 +156,12 @@ void parse_args(int argc, char** argv)
case 't':
sched_info_tag = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'm':
i_tbs_val = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'i':
i_sf_val = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'm':
i_tbs_val = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'r':
i_rep_val = (uint32_t)strtol(argv[optind], NULL, 10);
break;
@ -180,7 +188,7 @@ void parse_args(int argc, char** argv)
if (!output_file_name && rf_freq == 0) {
usage(argv[0]);
printf("\nError! Either RF frequency or output filename need to be specified.\n");
printf("\nError: either RF frequency or output filename needs to be specified\n");
exit(-1);
}
@ -197,12 +205,12 @@ void base_init()
// init memory
sf_buffer = srsran_vec_cf_malloc(sf_n_re);
if (!sf_buffer) {
perror("malloc");
perror("Error: malloc for sf_buffer");
exit(-1);
}
output_buffer = srsran_vec_cf_malloc(sf_n_samples);
if (!output_buffer) {
perror("malloc");
perror("Error: malloc for output buffer");
exit(-1);
}
// open file or USRP
@ -218,13 +226,17 @@ void base_init()
}
} else {
#ifndef DISABLE_RF
printf("Opening RF device...\n");
if (srsran_rf_open(&radio, rf_args)) {
fprintf(stderr, "Error opening rf\n");
exit(-1);
if (strlen(rf_dev) > 0) {
if (srsran_rf_open_devname(&radio, rf_dev, rf_args, NOF_TX_ANT)) {
fprintf(stderr, "Error opening RF device %s\n", rf_dev);
exit(-1);
}
} else if (srsran_rf_open(&radio, rf_args)) {
fprintf(stderr, "Error opening RF default device\n");
exit(-1);
}
#else
printf("Error RF not available. Select an output file\n");
printf("Error: RF not available - select an output file\n");
exit(-1);
#endif
}
@ -355,7 +367,7 @@ static int update_radl(void)
srsran_ra_nbiot_dl_dci_to_grant(&ra_dl, &dummy_grant, DUMMY_SFN, DUMMY_SFIDX, DUMMY_R_MAX, false, cell.mode);
srsran_ra_nbiot_dl_grant_to_nbits(&dummy_grant, cell, 0, &dummy_nbits);
srsran_ra_nbiot_dl_grant_fprint(stdout, &dummy_grant);
printf("Type new MCS index and press Enter: ");
printf("Enter new MCS index: ");
fflush(stdout);
return SRSRAN_SUCCESS;
@ -382,7 +394,7 @@ static int update_control(void)
i_tbs_val = atoi(input);
bzero(input, sizeof(input));
if (update_radl()) {
printf("Trying with last known MCS index\n");
printf("Trying last known MCS index\n");
i_tbs_val = last_i_tbs_val;
return update_radl();
}
@ -390,7 +402,7 @@ static int update_control(void)
return 0;
} else if (n < 0) {
// error
perror("select");
perror("Error: select for MCS entry");
return -1;
} else {
return 0;
@ -459,7 +471,7 @@ int main(int argc, char** argv)
}
if (srsran_nbiot_ue_dl_set_cell(&ue_dl, cell)) {
fprintf(stderr, "Setting cell in UE DL\n");
fprintf(stderr, "Error setting cell in UE DL\n");
return -1;
}
@ -498,16 +510,17 @@ int main(int argc, char** argv)
signal(SIGINT, sig_int_handler);
if (!output_file_name) {
/* set sampling frequency */
int srate = srsran_sampling_freq_hz(cell.base.nof_prb);
if (srate != -1) {
printf("Setting sampling rate %.2f MHz\n", (float)srate / 1000000);
printf("Setting tx sampling rate %.2f MHz\n", (float)srate / 1000000);
float srate_rf = srsran_rf_set_tx_srate(&radio, (double)srate);
if (srate_rf != srate) {
fprintf(stderr, "Could not set sampling rate\n");
if (abs(srate - (int)srate_rf) > MAX_SRATE_DELTA) {
ERROR("Could not set tx sampling rate : wanted %d got %f", srate, srate_rf);
exit(-1);
}
} else {
fprintf(stderr, "Invalid number of PRB %d\n", cell.base.nof_prb);
fprintf(stderr, "Error: invalid number of PRB %d\n", cell.base.nof_prb);
exit(-1);
}
srsran_rf_set_tx_gain(&radio, rf_gain);
@ -517,6 +530,7 @@ int main(int argc, char** argv)
#endif
if (update_radl()) {
fprintf(stderr, "Error updating radl\n");
exit(-1);
}
@ -599,7 +613,7 @@ int main(int argc, char** argv)
}
if (srsran_nbiot_ue_dl_is_sib1_sf(&ue_dl, sfn, sf_idx)) {
INFO("%d.%d: Transmitting SIB1-NB.", sfn, sf_idx);
INFO("%d.%d: Transmitting SIB1-NB", sfn, sf_idx);
assert(send_data == false);
// configure DL grant for SIB1-NB transmission
@ -723,7 +737,7 @@ int main(int argc, char** argv)
base_free();
printf("Done\n");
printf("%s done\n", argv[0]);
return SRSRAN_SUCCESS;
}

@ -59,6 +59,8 @@ static char* output_file_name = NULL;
#define CFR_THRES_STEP 0.05f
#define CFR_PAPR_STEP 0.1f
#define MAX_SRATE_DELTA 2 // allowable delta (in Hz) between requested and actual sample rate
static srsran_cell_t cell = {
25, // nof_prb
1, // nof_ports
@ -965,12 +967,13 @@ int main(int argc, char** argv)
signal(SIGINT, sig_int_handler);
if (!output_file_name) {
/* set sampling frequency */
int srate = srsran_sampling_freq_hz(cell.nof_prb);
if (srate != -1) {
printf("Setting sampling rate %.2f MHz\n", (float)srate / 1000000);
printf("Setting tx sampling rate %.2f MHz\n", (float)srate / 1000000);
float srate_rf = srsran_rf_set_tx_srate(&radio, (double)srate);
if (srate_rf != srate) {
ERROR("Could not set sampling rate");
if (abs(srate - (int)srate_rf) > MAX_SRATE_DELTA) {
ERROR("Could not set tx sampling rate : wanted %d got %f", srate, srate_rf);
exit(-1);
}
} else {

@ -38,6 +38,8 @@
#define ENABLE_AGC_DEFAULT
#define MAX_SRATE_DELTA 2 // allowable delta (in Hz) between requested and actual sample rate
#ifndef DISABLE_RF
#include "srsran/phy/rf/rf.h"
@ -484,7 +486,7 @@ int main(int argc, char** argv)
signal(SIGINT, sig_int_handler);
/* set receiver frequency */
printf("Tunning receiver to %.3f MHz\n", (prog_args.rf_freq + prog_args.file_offset_freq) / 1000000);
printf("Tuning receiver to %.3f MHz\n", (prog_args.rf_freq + prog_args.file_offset_freq) / 1000000);
srsran_rf_set_rx_freq(&rf, prog_args.rf_nof_rx_ant, prog_args.rf_freq + prog_args.file_offset_freq);
uint32_t ntrial = 0;
@ -495,7 +497,7 @@ int main(int argc, char** argv)
ERROR("Error searching for cell");
exit(-1);
} else if (ret == 0 && !go_exit) {
printf("Cell not found after %d trials. Trying again (Press Ctrl+C to exit)\n", ntrial++);
printf("Cell not found after [%4d] attempts. Trying again... (Ctrl+C to exit)\n", ntrial++);
}
} while (ret == 0 && !go_exit);
@ -507,10 +509,10 @@ int main(int argc, char** argv)
/* set sampling frequency */
int srate = srsran_sampling_freq_hz(cell.nof_prb);
if (srate != -1) {
printf("Setting sampling rate %.2f MHz\n", (float)srate / 1000000);
printf("Setting rx sampling rate %.2f MHz\n", (float)srate / 1000000);
float srate_rf = srsran_rf_set_rx_srate(&rf, (double)srate);
if (srate_rf != srate) {
ERROR("Could not set sampling rate");
if (abs(srate - (int)srate_rf) > MAX_SRATE_DELTA) {
ERROR("Could not set rx sampling rate : wanted %d got %f", srate, srate_rf);
exit(-1);
}
} else {

@ -43,6 +43,8 @@
#define PCAP_FILENAME "/tmp/pssch.pcap"
#define MAX_SRATE_DELTA 2 // allowable delta (in Hz) between requested and actual sample rate
static bool keep_running = true;
static srsran_cell_sl_t cell_sl = {.nof_prb = 50, .tm = SRSRAN_SIDELINK_TM4, .cp = SRSRAN_CP_NORM, .N_sl_id = 0};
@ -65,13 +67,12 @@ typedef struct {
void args_default(prog_args_t* args)
{
args->disable_plots = false;
args->use_standard_lte_rates = false;
args->disable_plots = false;
args->input_file_name = NULL;
args->file_start_sf_idx = 0;
args->nof_rx_antennas = 1;
args->rf_dev = "";
args->rf_dev = "";
args->rf_args = "";
args->rf_freq = 5.92e9;
args->rf_gain = 50;
@ -263,13 +264,14 @@ int main(int argc, char** argv)
printf("Set RX freq: %.6f MHz\n",
srsran_rf_set_rx_freq(&radio, prog_args.nof_rx_antennas, prog_args.rf_freq) / 1e6);
printf("Set RX gain: %.1f dB\n", prog_args.rf_gain);
int srate = srsran_sampling_freq_hz(cell_sl.nof_prb);
/* set sampling frequency */
int srate = srsran_sampling_freq_hz(cell_sl.nof_prb);
if (srate != -1) {
printf("Setting sampling rate %.2f MHz\n", (float)srate / 1000000);
float srate_rf = srsran_rf_set_rx_srate(&radio, (double)srate);
if (srate_rf != srate) {
ERROR("Could not set sampling rate");
if (abs(srate - (int)srate_rf) > MAX_SRATE_DELTA) {
ERROR("Could not set sampling rate : wanted %d got %f", srate, srate_rf);
exit(-1);
}
} else {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -80,6 +80,12 @@ struct ue_eutra_cap_s;
} // namespace rrc
} // namespace asn1
namespace srsenb {
struct ue_cell_ded;
} // namespace srsenb
/************************
* Conversion Helpers
***********************/
@ -139,7 +145,8 @@ int get_carrier_freq(const asn1::rrc::meas_obj_to_add_mod_s& obj);
/***************************
* EUTRA UE Capabilities
**************************/
rrc_ue_capabilities_t make_rrc_ue_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_cap_s);
rrc_ue_capabilities_t make_rrc_ue_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_cap_s,
const srsenb::ue_cell_ded& pcell);
// mbms
mbms_notif_cfg_t make_mbms_notif_cfg(const asn1::rrc::mbms_notif_cfg_r9_s& asn1_type);

@ -60,9 +60,16 @@ const uint32_t MAX_LTE_SRB_ID = 2;
enum class lte_drb { drb1 = 1, drb2, drb3, drb4, drb5, drb6, drb7, drb8, drb9, drb10, drb11, invalid };
const uint32_t MAX_LTE_DRB_ID = 11;
const uint32_t MAX_LTE_LCID = 10; // logicalChannelIdentity 3..10 in TS 36.331 v15.3
const uint32_t MAX_EPS_BEARER_ID = 15; // EPS Bearer ID range [5, 15] in 36 413
const uint32_t MIN_EPS_BEARER_ID = 5;
const uint32_t INVALID_LCID = 99; // random invalid LCID
const uint32_t INVALID_EPS_BEARER_ID = 99; // random invalid eps bearer id
constexpr bool is_eps_bearer_id(uint32_t eps_bearer_id)
{
return eps_bearer_id >= MIN_EPS_BEARER_ID and eps_bearer_id <= MAX_EPS_BEARER_ID;
}
constexpr bool is_lte_rb(uint32_t lcid)
{
return lcid <= MAX_LTE_LCID;
@ -97,6 +104,14 @@ inline const char* get_drb_name(lte_drb drb_id)
return names[(uint32_t)(drb_id < lte_drb::invalid ? drb_id : lte_drb::invalid) - 1];
}
inline const char* get_rb_name(uint32_t lcid)
{
if (is_lte_srb(lcid)) {
return get_srb_name(static_cast<lte_srb>(lcid));
}
return get_drb_name(static_cast<lte_drb>(lcid - MAX_LTE_SRB_ID));
}
} // namespace srsran
#endif // SRSRAN_COMMON_LTE_H

@ -66,6 +66,15 @@ enum class nr_drb {
const uint32_t MAX_NR_DRB_ID = 29;
const uint32_t MAX_NR_NOF_BEARERS = MAX_NR_DRB_ID + MAX_NR_SRB_ID; // 32
// PDU Session ID range [1, 15]. See TS 24.007, 11.2.3.1b.
const uint32_t MAX_NR_PDU_SESSION_ID = 15;
const uint32_t MIN_NR_PDU_SESSION_ID = 1;
constexpr bool is_nr_pdu_session_id(uint32_t pdu_session_id)
{
return pdu_session_id >= MIN_NR_PDU_SESSION_ID and pdu_session_id <= MAX_NR_PDU_SESSION_ID;
}
constexpr bool is_nr_lcid(uint32_t lcid)
{
return lcid < MAX_NR_NOF_BEARERS;
@ -103,6 +112,15 @@ inline const char* get_drb_name(nr_drb drb_id)
"DRB25", "DRB26", "DRB27", "DRB28", "DRB29", "invalid DRB id"};
return names[(uint32_t)(drb_id < nr_drb::invalid ? drb_id : nr_drb::invalid) - 1];
}
inline const char* get_nr_rb_name(uint32_t lcid)
{
if (is_nr_srb(lcid)) {
return get_srb_name(static_cast<nr_srb>(lcid));
}
return get_drb_name(static_cast<nr_drb>(lcid - MAX_NR_SRB_ID));
}
} // namespace srsran
#endif // SRSRAN_COMMON_NR_H

@ -0,0 +1,35 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsgnb/hdr/stack/ric/e2sm.h"
#include "srsran/common/common.h"
#include "srsran/common/interfaces_common.h"
#include "srsran/interfaces/enb_metrics_interface.h"
#include "srsran/srsran.h"
#ifndef SRSRAN_E2_INTERFACES_H
#define SRSRAN_E2_INTERFACES_H
namespace srsenb {
class e2_interface_metrics
{
public:
virtual bool pull_metrics(enb_metrics_t* m) = 0;
virtual bool register_e2sm(e2sm* sm) = 0;
virtual bool unregister_e2sm(e2sm* sm) = 0;
};
} // namespace srsenb
#endif // SRSRAN_ENB_INTERFACES_H

@ -75,6 +75,7 @@ struct cell_cfg_t {
asn1::rrc::mob_ctrl_info_s::t304_e_ t304;
std::vector<scell_cfg_t> scell_list;
rrc_meas_cfg_t meas_cfg;
bool barred;
};
typedef std::vector<cell_cfg_t> cell_list_t;

@ -458,6 +458,8 @@ struct rrc_ue_capabilities_t {
uint8_t category_ul = 0;
bool support_dl_256qam = false;
bool support_ul_64qam = false;
bool support_ca_bands = false;
bool support_ul_ca = false;
};
} // namespace srsran

@ -171,7 +171,7 @@ typedef enum { SRSRAN_SF_NORM = 0, SRSRAN_SF_MBSFN } srsran_sf_t;
#define SRSRAN_FDD_NOF_HARQ (FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS)
#define SRSRAN_MAX_HARQ_PROC 15
#define SRSRAN_NOF_LTE_BANDS 58
#define SRSRAN_NOF_LTE_BANDS 59
#define SRSRAN_DEFAULT_MAX_FRAMES_PBCH 500
#define SRSRAN_DEFAULT_MAX_FRAMES_PSS 10

@ -106,7 +106,6 @@ private:
srsran::timer_handler* timers = nullptr;
typedef std::map<uint16_t, std::unique_ptr<rlc_common> > rlc_map_t;
typedef std::pair<uint16_t, std::unique_ptr<rlc_common> > rlc_map_pair_t;
rlc_map_t rlc_array, rlc_array_mrb;
pthread_rwlock_t rwlock;

@ -78,5 +78,8 @@ add_library(nas_5g_msg STATIC nas_5g_msg.cc nas_5g_ies.cc nas_5g_utils.cc)
target_compile_options(nas_5g_msg PRIVATE "-Os")
target_link_libraries(nas_5g_msg asn1_utils srsran_common)
install(TARGETS nas_5g_msg DESTINATION ${LIBRARY_DIR} OPTIONAL)
## ORAN E2 RIC
add_library(ric_e2 STATIC e2sm_kpm_v2.cpp e2ap.cpp e2sm.cpp)
target_compile_options(ric_e2 PRIVATE "-Os")
target_link_libraries(ric_e2 asn1_utils srsran_common)
install(TARGETS ric_e2 DESTINATION ${LIBRARY_DIR} OPTIONAL)

@ -1285,9 +1285,7 @@ pack(bit_ref& bref, const std::string& s, size_t lb, size_t ub, size_t alb, size
size_t b = asn_string_utils::get_nof_bits_per_char(lb, ub, aligned);
bool octet_aligned = asn_string_utils::is_octet_aligned(b, alb, aub, aligned);
bool length_encoded = asn_string_utils::is_length_encoded(alb, aub, aligned);
if (octet_aligned) {
bref.align_bytes_zero();
}
if (ext) {
HANDLE_CODE(bref.pack(0, 1));
}
@ -1309,9 +1307,7 @@ SRSASN_CODE unpack(std::string& s, cbit_ref& bref, size_t lb, size_t ub, size_t
bool octet_aligned = asn_string_utils::is_octet_aligned(b, alb, aub, aligned);
bool length_encoded = asn_string_utils::is_length_encoded(alb, aub, aligned);
size_t max_nof_bits = b * aub;
if (octet_aligned) {
bref.align_bytes();
}
if (ext) {
bool is_ext;
HANDLE_CODE(bref.unpack(is_ext, 1));

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -612,6 +612,9 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_plmn_list_ie(uint8** ie_ptr, LIBLTE_MME_PLMN
if (ie_ptr != NULL && plmn_list != NULL) {
plmn_list->N_plmns = (*ie_ptr)[0] / 3;
if (plmn_list->N_plmns > LIBLTE_MME_PLMN_LIST_MAX_SIZE) {
return (err);
}
for (i = 0; i < plmn_list->N_plmns; i++) {
plmn_list->mcc[i] = ((*ie_ptr)[i * 3 + 0] & 0x0F) * 100;
plmn_list->mcc[i] += (((*ie_ptr)[i * 3 + 0] >> 4) & 0x0F) * 10;
@ -3213,7 +3216,7 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_generic_message_container_ie(uint8** ie_ptr,
msg->N_bytes |= (*ie_ptr)[1];
if (msg->N_bytes > LIBLTE_MAX_MSG_SIZE_BYTES) {
return err;
return (err);
}
for (i = 0; i < msg->N_bytes; i++) {

@ -20,6 +20,7 @@
*/
#include "srsran/asn1/rrc_utils.h"
#include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h"
#include "srsran/asn1/obj_id_cmp_utils.h"
#include "srsran/asn1/rrc.h"
#include "srsran/config.h"
@ -932,36 +933,144 @@ int get_carrier_freq(const asn1::rrc::meas_obj_to_add_mod_s& obj)
*/
template <class T>
static void set_rrc_ue_eutra_cap_t_gen(rrc_ue_capabilities_t& ue_cap, const T& ue_eutra_cap)
static void
set_rrc_ue_eutra_cap_t_gen(rrc_ue_capabilities_t& ue_cap, const srsenb::ue_cell_ded& pcell, const T& ue_eutra_cap)
{
if (ue_eutra_cap.non_crit_ext_present) {
set_rrc_ue_eutra_cap_t_gen(ue_cap, ue_eutra_cap.non_crit_ext);
set_rrc_ue_eutra_cap_t_gen(ue_cap, pcell, ue_eutra_cap.non_crit_ext);
}
}
static void set_rrc_ue_eutra_cap_t_gen(rrc_ue_capabilities_t& ue_cap, const asn1::rrc::ue_eutra_cap_s& ue_eutra_cap)
static void set_rrc_ue_eutra_cap_t_gen(rrc_ue_capabilities_t& ue_cap,
const srsenb::ue_cell_ded& pcell,
const asn1::rrc::ue_eutra_cap_s& ue_eutra_cap)
{
ue_cap.release = ue_eutra_cap.access_stratum_release.to_number();
ue_cap.category = ue_eutra_cap.ue_category;
if (ue_eutra_cap.non_crit_ext_present) {
set_rrc_ue_eutra_cap_t_gen(ue_cap, ue_eutra_cap.non_crit_ext);
set_rrc_ue_eutra_cap_t_gen(ue_cap, pcell, ue_eutra_cap.non_crit_ext);
}
}
bool is_ca_band_combo_supported(const band_combination_params_r10_l& enb_band_combo,
const band_combination_params_r10_l& ue_band_combo)
{
if (enb_band_combo.size() != ue_band_combo.size()) {
return false;
}
for (unsigned i = 0; i < enb_band_combo.size(); ++i) {
if (ue_band_combo[i].band_eutra_r10 != enb_band_combo[i].band_eutra_r10) {
return false;
}
}
for (unsigned i = 0; i < enb_band_combo.size(); ++i) {
const auto& enb_band = enb_band_combo[i];
const auto& ue_band = ue_band_combo[i];
if (enb_band.band_params_dl_r10_present and !ue_band.band_params_dl_r10_present) {
return false;
}
// for SCells this depends on the ul_allowed parameter
if (i == 0 and enb_band.band_params_ul_r10_present and !ue_band.band_params_ul_r10_present) {
return false;
}
if (enb_band.band_params_dl_r10_present and ue_band.band_params_dl_r10_present) {
if (enb_band.band_params_dl_r10.size() != ue_band.band_params_dl_r10.size()) {
return false;
}
for (unsigned j = 0; j < enb_band.band_params_dl_r10.size(); ++j) {
if (enb_band.band_params_dl_r10[j].ca_bw_class_dl_r10 > ue_band.band_params_dl_r10[j].ca_bw_class_dl_r10) {
return false;
}
}
}
if (enb_band.band_params_ul_r10_present and ue_band.band_params_ul_r10_present) {
if (enb_band.band_params_ul_r10.size() != ue_band.band_params_ul_r10.size()) {
return false;
}
for (unsigned j = 0; j < enb_band.band_params_ul_r10.size(); ++j) {
if (enb_band.band_params_ul_r10[j].ca_bw_class_ul_r10 > ue_band.band_params_ul_r10[j].ca_bw_class_ul_r10) {
return false;
}
}
}
}
return true;
}
bool is_ul_ca_supported(const band_combination_params_r10_l& ue_band_combo)
{
uint32_t ul_band_num = 0;
for (const auto& ue_band : ue_band_combo) {
if (ue_band.band_params_ul_r10_present) {
ul_band_num++;
}
}
return ul_band_num == ue_band_combo.size();
}
static void set_rrc_ue_eutra_cap_t_gen(rrc_ue_capabilities_t& ue_cap,
const srsenb::ue_cell_ded& pcell,
const asn1::rrc::ue_eutra_cap_v1020_ies_s& ue_eutra_cap)
{
if (ue_eutra_cap.ue_category_v1020_present) {
ue_cap.category = ue_eutra_cap.ue_category_v1020;
}
if (ue_eutra_cap.rf_params_v1020_present) {
const asn1::rrc::rf_params_v1020_s& rf_params = ue_eutra_cap.rf_params_v1020;
const srsenb::enb_cell_common* pcell_cfg = pcell.cell_common;
uint32_t cc_num = 1 + pcell_cfg->scells.size();
band_combination_params_r10_l enb_band_combo;
// TODO: add proper class (currently hardcoded class A) and mimo_cap checks
for (unsigned i = 0; i < cc_num; ++i) {
ca_mimo_params_dl_r10_s ca_mimo_params_dl;
ca_mimo_params_dl.ca_bw_class_dl_r10 = ca_bw_class_r10_e::a;
ca_mimo_params_dl.supported_mimo_cap_dl_r10_present = false;
ca_mimo_params_dl.supported_mimo_cap_dl_r10 = mimo_cap_dl_r10_opts::nulltype;
ca_mimo_params_ul_r10_s ca_mimo_params_ul;
ca_mimo_params_ul.ca_bw_class_ul_r10 = ca_bw_class_r10_e::a;
ca_mimo_params_ul.supported_mimo_cap_ul_r10_present = false;
ca_mimo_params_ul.supported_mimo_cap_ul_r10 = mimo_cap_ul_r10_opts::nulltype;
band_params_r10_s band_params;
uint32_t dl_earfcn = i == 0 ? pcell_cfg->cell_cfg.dl_earfcn : pcell_cfg->scells[i - 1]->cell_cfg.dl_earfcn;
band_params.band_eutra_r10 = (uint8_t)srsran_band_get_band(dl_earfcn);
band_params.band_params_dl_r10_present = true;
band_params.band_params_dl_r10.push_back(ca_mimo_params_dl);
// PCell always supports UL, SCell depending on the config
if (i == 0 or (i >= 1 and pcell_cfg->cell_cfg.scell_list[i - 1].ul_allowed)) {
band_params.band_params_ul_r10_present = true;
band_params.band_params_ul_r10.push_back(ca_mimo_params_ul);
}
enb_band_combo.push_back(band_params);
}
// compare the currently used CA band combo with band combos from UE
for (const auto& ue_band_combo : rf_params.supported_band_combination_r10) {
ue_cap.support_ca_bands |= is_ca_band_combo_supported(enb_band_combo, ue_band_combo);
if (ue_cap.support_ca_bands) {
ue_cap.support_ul_ca = is_ul_ca_supported(ue_band_combo);
break;
}
}
}
if (ue_eutra_cap.non_crit_ext_present) {
set_rrc_ue_eutra_cap_t_gen(ue_cap, ue_eutra_cap.non_crit_ext);
set_rrc_ue_eutra_cap_t_gen(ue_cap, pcell, ue_eutra_cap.non_crit_ext);
}
}
static void set_rrc_ue_eutra_cap_t_gen(rrc_ue_capabilities_t& ue_cap,
const srsenb::ue_cell_ded& pcell,
const asn1::rrc::ue_eutra_cap_v1250_ies_s& ue_eutra_cap)
{
if (ue_eutra_cap.ue_category_dl_r12_present) {
@ -985,20 +1094,22 @@ static void set_rrc_ue_eutra_cap_t_gen(rrc_ue_capabilities_t&
}
if (ue_eutra_cap.non_crit_ext_present) {
set_rrc_ue_eutra_cap_t_gen(ue_cap, ue_eutra_cap.non_crit_ext);
set_rrc_ue_eutra_cap_t_gen(ue_cap, pcell, ue_eutra_cap.non_crit_ext);
}
}
static void set_rrc_ue_eutra_cap_t_gen(rrc_ue_capabilities_t& ue_cap,
const srsenb::ue_cell_ded& pcell,
const asn1::rrc::ue_eutra_cap_v1530_ies_s& ue_eutra_cap)
{
; // Do nothing
}
rrc_ue_capabilities_t make_rrc_ue_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_cap_s)
rrc_ue_capabilities_t make_rrc_ue_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_cap_s,
const srsenb::ue_cell_ded& pcell)
{
rrc_ue_capabilities_t ue_cap;
set_rrc_ue_eutra_cap_t_gen(ue_cap, eutra_cap_s);
set_rrc_ue_eutra_cap_t_gen(ue_cap, pcell, eutra_cap_s);
ue_cap.support_ul_64qam |= (ue_cap.category == 5) or (ue_cap.category == 8 and ue_cap.release >= 10);
return ue_cap;
}

@ -587,7 +587,8 @@ struct lte_band lte_bands[SRSRAN_NOF_LTE_BANDS] = {
{68, 753, 67536, 132672, 55, SRSRAN_BAND_GEO_AREA_EMEA},
{69, 2570, 67836, 0, 0, SRSRAN_BAND_GEO_AREA_EMEA},
{70, 1995, 68336, 132972, 300, SRSRAN_BAND_GEO_AREA_NAR},
{71, 0, 68586, 133122, 0, SRSRAN_BAND_GEO_AREA_NAR} // dummy band to bound band 70 earfcn
{71, 617, 68586, 133122, -46, SRSRAN_BAND_GEO_AREA_NAR},
{72, 0, 68936, 133472, 0, SRSRAN_BAND_GEO_AREA_NAR} // dummy band to bound band 71 earfcn
};
int srsran_str2mimotype(char* mimo_type_str, srsran_tx_scheme_t* type)

@ -453,7 +453,7 @@ int rlc::add_bearer(uint32_t lcid, const rlc_config_t& cnfg)
rlc_entity->set_bsr_callback(bsr_callback);
if (not rlc_array.insert(rlc_map_pair_t(lcid, std::move(rlc_entity))).second) {
if (not rlc_array.emplace(lcid, std::move(rlc_entity)).second) {
logger.error("Error inserting RLC entity in to array.");
return SRSRAN_ERROR;
}
@ -476,7 +476,7 @@ int rlc::add_bearer_mrb(uint32_t lcid)
}
rlc_entity->set_bsr_callback(bsr_callback);
if (rlc_array_mrb.count(lcid) == 0) {
if (not rlc_array_mrb.insert(rlc_map_pair_t(lcid, std::move(rlc_entity))).second) {
if (not rlc_array_mrb.emplace(lcid, std::move(rlc_entity)).second) {
logger.error("Error inserting RLC entity in to array.");
return SRSRAN_ERROR;
}
@ -526,7 +526,7 @@ void rlc::change_lcid(uint32_t old_lcid, uint32_t new_lcid)
// insert old rlc entity into new LCID
rlc_map_t::iterator it = rlc_array.find(old_lcid);
std::unique_ptr<rlc_common> rlc_entity = std::move(it->second);
if (not rlc_array.insert(rlc_map_pair_t(new_lcid, std::move(rlc_entity))).second) {
if (not rlc_array.emplace(new_lcid, std::move(rlc_entity)).second) {
logger.error("Error inserting RLC entity into array.");
return;
}

@ -66,6 +66,31 @@ int rrc_conn_reconfig_ho_test1()
return 0;
}
int rrc_ue_cap_enquiry_test()
{
uint8_t rrc_msg[] = {0x38, 0x00, 0x00};
// 38 00 00
cbit_ref bref(rrc_msg, sizeof(rrc_msg));
dl_dcch_msg_s dl_dcch_msg;
dl_dcch_msg.unpack(bref);
TESTASSERT(dl_dcch_msg.msg.type() == dl_dcch_msg_type_c::types::c1);
TESTASSERT(dl_dcch_msg.msg.c1().type() == dl_dcch_msg_type_c::c1_c_::types::ue_cap_enquiry);
// assign to stack-allocated variable
asn1::rrc::ue_cap_enquiry_s ue_cap;
ue_cap = dl_dcch_msg.msg.c1().ue_cap_enquiry();
TESTASSERT(ue_cap.crit_exts.c1().type() ==
asn1::rrc::ue_cap_enquiry_s::crit_exts_c_::c1_c_::types::ue_cap_enquiry_r8);
TESTASSERT(ue_cap.crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request.size() == 1);
TESTASSERT(ue_cap.crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request[0] == asn1::rrc::rat_type_e::eutra);
return 0;
}
int main(int argc, char** argv)
{
auto& asn1_logger = srslog::fetch_basic_logger("ASN1", false);
@ -75,6 +100,7 @@ int main(int argc, char** argv)
srslog::init();
TESTASSERT(rrc_conn_reconfig_ho_test1() == 0);
TESTASSERT(rrc_ue_cap_enquiry_test() == 0);
return 0;
}

@ -24,6 +24,7 @@
#include "srsran/common/mac_pcap.h"
#include <iostream>
using namespace asn1;
using namespace asn1::rrc;
#define PCAP 0
@ -126,6 +127,352 @@ int rrc_ue_cap_info_test(srsran::mac_pcap* pcap)
return 0;
}
int rrc_ue_cap_info_pack_buff_size_test(srsran::mac_pcap* pcap, const uint32_t buf_size)
{
auto& rrc_logger = srslog::fetch_basic_logger("RRC", false);
rrc_logger.set_level(srslog::basic_levels::debug);
rrc_logger.set_hex_dump_max_size(128);
srsue::rrc_args_t args = {};
args.ue_category = 8;
args.ue_category_ul = 5;
args.ue_category_dl = 14;
args.release = 15;
args.feature_group = 0xe6041c00;
args.nof_supported_bands = 1;
args.supported_bands[0] = 8;
args.nof_lte_carriers = 4;
args.nof_nr_carriers = 0;
args.support_ca = true;
args.supported_bands_nr.push_back(3);
asn1::rrc::ul_dcch_msg_s ul_dcch_msg;
ul_dcch_msg.msg.set(ul_dcch_msg_type_c::types::c1);
ul_dcch_msg.msg.c1().set(ul_dcch_msg_type_c::c1_c_::types::ue_cap_info);
ul_dcch_msg.msg.c1().ue_cap_info().rrc_transaction_id = 0;
ul_dcch_msg.msg.c1().ue_cap_info().crit_exts.set(ue_cap_info_s::crit_exts_c_::types::c1);
ul_dcch_msg.msg.c1().ue_cap_info().crit_exts.c1().set(ue_cap_info_s::crit_exts_c_::c1_c_::types::ue_cap_info_r8);
ue_cap_info_r8_ies_s* info = &ul_dcch_msg.msg.c1().ue_cap_info().crit_exts.c1().ue_cap_info_r8();
info->ue_cap_rat_container_list.resize(1);
info->ue_cap_rat_container_list[0].rat_type = rat_type_e::eutra;
ue_eutra_cap_s cap;
cap.access_stratum_release = (access_stratum_release_e::options)(args.release - SRSRAN_RELEASE_MIN);
cap.ue_category = (uint8_t)((args.ue_category < 1 || args.ue_category > 5) ? 4 : args.ue_category);
cap.pdcp_params.max_num_rohc_context_sessions_present = false;
cap.pdcp_params.supported_rohc_profiles.profile0x0001_r15 = false;
cap.pdcp_params.supported_rohc_profiles.profile0x0002_r15 = false;
cap.pdcp_params.supported_rohc_profiles.profile0x0003_r15 = false;
cap.pdcp_params.supported_rohc_profiles.profile0x0004_r15 = false;
cap.pdcp_params.supported_rohc_profiles.profile0x0006_r15 = false;
cap.pdcp_params.supported_rohc_profiles.profile0x0101_r15 = false;
cap.pdcp_params.supported_rohc_profiles.profile0x0102_r15 = false;
cap.pdcp_params.supported_rohc_profiles.profile0x0103_r15 = false;
cap.pdcp_params.supported_rohc_profiles.profile0x0104_r15 = false;
cap.phy_layer_params.ue_specific_ref_sigs_supported = false;
cap.phy_layer_params.ue_tx_ant_sel_supported = false;
cap.rf_params.supported_band_list_eutra.resize(args.nof_supported_bands);
cap.meas_params.band_list_eutra.resize(args.nof_supported_bands);
for (uint32_t k = 0; k < args.nof_supported_bands; k++) {
cap.rf_params.supported_band_list_eutra[k].band_eutra = args.supported_bands[k];
cap.rf_params.supported_band_list_eutra[k].half_duplex = false;
cap.meas_params.band_list_eutra[k].inter_freq_band_list.resize(1);
cap.meas_params.band_list_eutra[k].inter_freq_band_list[0].inter_freq_need_for_gaps = true;
}
cap.feature_group_inds_present = true;
cap.feature_group_inds.from_number(args.feature_group);
ue_eutra_cap_v1280_ies_s* ue_eutra_cap_v1280_ies;
ue_eutra_cap_v1360_ies_s* ue_eutra_cap_v1360_ies;
ue_eutra_cap_v1450_ies_s* ue_eutra_cap_v1450_ies;
if (args.release > 8) {
ue_eutra_cap_v920_ies_s cap_v920;
cap_v920.phy_layer_params_v920.enhanced_dual_layer_fdd_r9_present = false;
cap_v920.phy_layer_params_v920.enhanced_dual_layer_tdd_r9_present = false;
cap_v920.inter_rat_params_geran_v920.dtm_r9_present = false;
cap_v920.inter_rat_params_geran_v920.e_redirection_geran_r9_present = false;
cap_v920.csg_proximity_ind_params_r9.inter_freq_proximity_ind_r9_present = false;
cap_v920.csg_proximity_ind_params_r9.intra_freq_proximity_ind_r9_present = false;
cap_v920.csg_proximity_ind_params_r9.utran_proximity_ind_r9_present = false;
cap_v920.neigh_cell_si_acquisition_params_r9.inter_freq_si_acquisition_for_ho_r9_present = false;
cap_v920.neigh_cell_si_acquisition_params_r9.intra_freq_si_acquisition_for_ho_r9_present = false;
cap_v920.neigh_cell_si_acquisition_params_r9.utran_si_acquisition_for_ho_r9_present = false;
cap_v920.son_params_r9.rach_report_r9_present = false;
cap.non_crit_ext_present = true;
cap.non_crit_ext = cap_v920;
}
if (args.release > 9) {
phy_layer_params_v1020_s phy_layer_params_v1020;
phy_layer_params_v1020.two_ant_ports_for_pucch_r10_present = false;
phy_layer_params_v1020.tm9_with_minus8_tx_fdd_r10_present = false;
phy_layer_params_v1020.pmi_disabling_r10_present = false;
phy_layer_params_v1020.cross_carrier_sched_r10_present = args.support_ca;
phy_layer_params_v1020.simul_pucch_pusch_r10_present = false;
phy_layer_params_v1020.multi_cluster_pusch_within_cc_r10_present = false;
phy_layer_params_v1020.non_contiguous_ul_ra_within_cc_list_r10_present = false;
rf_params_v1020_s rf_params;
band_combination_params_r10_l combination_params;
if (args.support_ca) {
// add Intraband Contiguous or Interband Non-contiguous CA band combination
// note that nof_supported_bands=1 when all cells are in the same but non-contiguous band
for (uint32_t k = 0; k < args.nof_supported_bands; k++) {
ca_mimo_params_dl_r10_s ca_mimo_params_dl;
ca_mimo_params_dl.ca_bw_class_dl_r10 = ca_bw_class_r10_e::f;
ca_mimo_params_dl.supported_mimo_cap_dl_r10_present = false;
ca_mimo_params_ul_r10_s ca_mimo_params_ul;
ca_mimo_params_ul.ca_bw_class_ul_r10 = ca_bw_class_r10_e::f;
ca_mimo_params_ul.supported_mimo_cap_ul_r10_present = false;
band_params_r10_s band_params;
band_params.band_eutra_r10 = args.supported_bands[k];
band_params.band_params_dl_r10_present = true;
band_params.band_params_dl_r10.push_back(ca_mimo_params_dl);
band_params.band_params_ul_r10_present = true;
band_params.band_params_ul_r10.push_back(ca_mimo_params_ul);
combination_params.push_back(band_params);
}
}
rf_params.supported_band_combination_r10.push_back(combination_params);
// add all 2CC, 3CC and 4CC Intraband Non-contiguous CA band combinations
for (uint32_t k = 0; k < args.nof_supported_bands; k++) {
for (uint32_t j = 2; j <= args.nof_lte_carriers; j++) {
combination_params.clear();
ca_mimo_params_dl_r10_s ca_mimo_params_dl;
ca_mimo_params_dl.ca_bw_class_dl_r10 = ca_bw_class_r10_e::a;
ca_mimo_params_dl.supported_mimo_cap_dl_r10_present = false;
ca_mimo_params_ul_r10_s ca_mimo_params_ul;
ca_mimo_params_ul.ca_bw_class_ul_r10 = ca_bw_class_r10_e::a;
ca_mimo_params_ul.supported_mimo_cap_ul_r10_present = false;
band_params_r10_s band_params;
band_params.band_eutra_r10 = args.supported_bands[k];
band_params.band_params_dl_r10_present = true;
band_params.band_params_dl_r10.push_back(ca_mimo_params_dl);
band_params.band_params_ul_r10_present = true;
band_params.band_params_ul_r10.push_back(ca_mimo_params_ul);
for (uint32_t l = 0; l < j; l++) {
combination_params.push_back(band_params);
}
rf_params.supported_band_combination_r10.push_back(combination_params);
}
}
ue_eutra_cap_v1020_ies_s cap_v1020;
if (args.ue_category >= 6 && args.ue_category <= 8) {
cap_v1020.ue_category_v1020_present = true;
cap_v1020.ue_category_v1020 = (uint8_t)args.ue_category;
} else {
// Do not populate UE category for this release if the category is out of range
}
cap_v1020.phy_layer_params_v1020_present = true;
cap_v1020.phy_layer_params_v1020 = phy_layer_params_v1020;
cap_v1020.rf_params_v1020_present = args.support_ca;
cap_v1020.rf_params_v1020 = rf_params;
ue_eutra_cap_v940_ies_s cap_v940;
cap_v940.non_crit_ext_present = true;
cap_v940.non_crit_ext = cap_v1020;
cap.non_crit_ext.non_crit_ext_present = true;
cap.non_crit_ext.non_crit_ext = cap_v940;
}
if (args.release > 10) {
ue_eutra_cap_v11a0_ies_s cap_v11a0;
if (args.ue_category >= 11 && args.ue_category <= 12) {
cap_v11a0.ue_category_v11a0 = (uint8_t)args.ue_category;
cap_v11a0.ue_category_v11a0_present = true;
} else {
// Do not populate UE category for this release if the category is out of range
}
ue_eutra_cap_v1180_ies_s cap_v1180;
cap_v1180.non_crit_ext_present = true;
cap_v1180.non_crit_ext = cap_v11a0;
ue_eutra_cap_v1170_ies_s cap_v1170;
cap_v1170.non_crit_ext_present = true;
cap_v1170.non_crit_ext = cap_v1180;
if (args.ue_category >= 9 && args.ue_category <= 10) {
cap_v1170.ue_category_v1170 = (uint8_t)args.ue_category;
cap_v1170.ue_category_v1170_present = true;
} else {
// Do not populate UE category for this release if the category is out of range
}
ue_eutra_cap_v1130_ies_s cap_v1130;
cap_v1130.non_crit_ext_present = true;
cap_v1130.non_crit_ext = cap_v1170;
ue_eutra_cap_v1090_ies_s cap_v1090;
cap_v1090.non_crit_ext_present = true;
cap_v1090.non_crit_ext = cap_v1130;
ue_eutra_cap_v1060_ies_s cap_v1060;
cap_v1060.non_crit_ext_present = true;
cap_v1060.non_crit_ext = cap_v1090;
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true;
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext = cap_v1060;
}
if (args.release > 11) {
supported_band_list_eutra_v1250_l supported_band_list_eutra_v1250;
for (uint32_t k = 0; k < args.nof_supported_bands; k++) {
supported_band_eutra_v1250_s supported_band_eutra_v1250;
// According to 3GPP 36.306 v12 Table 4.1A-1, 256QAM is supported for ue_category_dl 11-16
supported_band_eutra_v1250.dl_minus256_qam_r12_present = (args.ue_category_dl >= 11);
// According to 3GPP 36.331 v12 UE-EUTRA-Capability field descriptions
// This field is only present when the field ue-CategoryUL is considered to 5 or 13.
supported_band_eutra_v1250.ul_minus64_qam_r12_present = true;
supported_band_list_eutra_v1250.push_back(supported_band_eutra_v1250);
}
rf_params_v1250_s rf_params_v1250;
rf_params_v1250.supported_band_list_eutra_v1250_present = true;
rf_params_v1250.supported_band_list_eutra_v1250 = supported_band_list_eutra_v1250;
ue_eutra_cap_v1250_ies_s cap_v1250;
// Optional UE Category UL/DL
// Warning: Make sure the UE Category UL/DL matches with 3GPP 36.306 Table 4.1A-6
if (args.ue_category_dl >= 0) {
cap_v1250.ue_category_dl_r12_present = true;
cap_v1250.ue_category_dl_r12 = (uint8_t)args.ue_category_dl;
} else {
// Do not populate UE category for this release if the category is not available
}
if (args.ue_category_ul >= 0) {
cap_v1250.ue_category_ul_r12_present = true;
cap_v1250.ue_category_ul_r12 = (uint8_t)args.ue_category_ul;
} else {
// Do not populate UE category for this release if the category is not available
}
cap_v1250.rf_params_v1250_present = true;
cap_v1250.rf_params_v1250 = rf_params_v1250;
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext
.non_crit_ext.non_crit_ext_present = true;
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext
.non_crit_ext.non_crit_ext = cap_v1250;
// 12.50
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext
.non_crit_ext.non_crit_ext.non_crit_ext_present = true;
// 12.60
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext
.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true;
// 12.70
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext
.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true;
}
// Release 13
if (args.release > 12) {
// 12.80
ue_eutra_cap_v1280_ies =
&cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext
.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext;
ue_eutra_cap_v1280_ies->non_crit_ext_present = true;
// 13.10
ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext_present = true;
// 13.20
ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext.non_crit_ext_present = true;
// 13.30
ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true;
// 13.40
ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true;
// 13.50
ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present =
true;
}
// Release 14
if (args.release > 13) {
// 13.60
ue_eutra_cap_v1360_ies =
&ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext;
ue_eutra_cap_v1360_ies->non_crit_ext_present = true;
// 14.30
ue_eutra_cap_v1360_ies->non_crit_ext.non_crit_ext_present = true;
// 14.40
ue_eutra_cap_v1360_ies->non_crit_ext.non_crit_ext.non_crit_ext_present = true;
// 14.50
ue_eutra_cap_v1360_ies->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true;
}
// Release 15
if (args.release > 14) {
ue_eutra_cap_v1450_ies = &ue_eutra_cap_v1360_ies->non_crit_ext.non_crit_ext.non_crit_ext;
// 14.60
ue_eutra_cap_v1450_ies->non_crit_ext_present = true;
ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext_present = true;
irat_params_nr_r15_s irat_params_nr_r15;
irat_params_nr_r15.en_dc_r15_present = true;
irat_params_nr_r15.supported_band_list_en_dc_r15_present = true;
uint32_t nof_supported_nr_bands = args.supported_bands_nr.size();
irat_params_nr_r15.supported_band_list_en_dc_r15.resize(nof_supported_nr_bands);
for (uint32_t k = 0; k < nof_supported_nr_bands; k++) {
irat_params_nr_r15.supported_band_list_en_dc_r15[k].band_nr_r15 = args.supported_bands_nr[k];
}
ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext.irat_params_nr_r15_present = true;
ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext.irat_params_nr_r15 = irat_params_nr_r15;
ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext.non_crit_ext_present = true;
// 15.10
ue_eutra_cap_v1510_ies_s* ue_cap_enquiry_v1510_ies = &ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext;
ue_cap_enquiry_v1510_ies->pdcp_params_nr_r15_present = true;
ue_cap_enquiry_v1510_ies->pdcp_params_nr_r15.sn_size_lo_r15_present = true;
}
// Pack caps and copy to cap info
uint8_t buf[128];
bzero(buf, sizeof(buf));
asn1::bit_ref bref(buf, buf_size);
if (cap.pack(bref) != asn1::SRSASN_SUCCESS) {
rrc_logger.debug("Error packing EUTRA capabilities");
return -1;
}
bref.align_bytes_zero();
uint32_t cap_len = (uint32_t)bref.distance_bytes(buf);
info->ue_cap_rat_container_list[0].ue_cap_rat_container.resize(cap_len);
memcpy(info->ue_cap_rat_container_list[0].ue_cap_rat_container.data(), buf, cap_len);
rrc_logger.debug(buf, cap_len, "UE-Cap (%d/%zd B)", cap_len, sizeof(buf));
// pack the message
uint8_t byte_buf[512];
bzero(byte_buf, sizeof(byte_buf));
asn1::bit_ref bref3(byte_buf, sizeof(byte_buf));
ul_dcch_msg.pack(bref3);
bref3.align_bytes_zero();
uint32_t len = (uint32_t)bref3.distance_bytes(byte_buf);
rrc_logger.debug(byte_buf, len, "UL-DCCH (%d/%zd B)", len, sizeof(byte_buf));
if (pcap != NULL) {
pcap->write_ul_rrc_pdu(byte_buf, len);
}
return 0;
}
int pack_fail_test()
{
srsue::rrc_args_t args = {};
@ -175,6 +522,147 @@ int pack_fail_test()
return 0;
}
int rrc_ue_cap_information_test()
{
uint8_t rrc_msg[] = {
0x38, 0x01, 0x08, 0x89, 0x9e, 0x01, 0xb8, 0x05, 0x18, 0x18, 0x01, 0x33, 0xe6, 0xc0, 0x82, 0x06, 0x10, 0x38, 0xb1,
0x84, 0x08, 0x92, 0x30, 0x65, 0x2a, 0x64, 0xea, 0x14, 0xaa, 0x5e, 0xfc, 0xc1, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff,
0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f,
0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9,
0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff,
0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff,
0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xf9, 0x8f, 0xff, 0xff, 0xfb, 0xf0,
0x6e, 0xc4, 0xf0, 0x01, 0x41, 0xbc, 0x05, 0xdc, 0x80, 0x00, 0x00, 0x00, 0x2f, 0x60, 0x00, 0x00, 0x02, 0x02, 0x77,
0xc0, 0x00, 0x00, 0x00, 0xbf, 0xf0, 0x03, 0x00, 0x21, 0x00, 0x30, 0x02, 0x00, 0x80, 0x14, 0x00, 0xc0, 0x0c, 0x00,
0x90, 0x03, 0x04, 0x21, 0x80, 0x18, 0x01, 0x82, 0x10, 0xc1, 0x08, 0x60, 0x80, 0x30, 0x40, 0x18, 0x20, 0x0c, 0x10,
0x86, 0x08, 0x43, 0x04, 0x21, 0x82, 0x10, 0xc1, 0x00, 0x60, 0x80, 0x24, 0x10, 0x06, 0x08, 0x03, 0x04, 0x20, 0x82,
0x10, 0x41, 0x08, 0x60, 0x84, 0x30, 0x42, 0x18, 0x21, 0x0c, 0x10, 0x82, 0x08, 0x41, 0x04, 0x21, 0x41, 0x00, 0x60,
0x80, 0x30, 0x40, 0x12, 0x08, 0x43, 0x04, 0x21, 0x82, 0x10, 0xc1, 0x08, 0x60, 0x84, 0x30, 0x42, 0x18, 0x20, 0x0c,
0x10, 0x04, 0x20, 0x80, 0x30, 0x40, 0x18, 0x20, 0x0c, 0x10, 0x06, 0x08, 0x43, 0x04, 0x21, 0x41, 0x08, 0x20, 0x84,
0x10, 0x42, 0x08, 0x21, 0x04, 0x10, 0x06, 0x08, 0x02, 0x41, 0x00, 0x40, 0x82, 0x00, 0xc1, 0x00, 0x60, 0x80, 0x30,
0x40, 0x12, 0x08, 0x41, 0x04, 0x20, 0x02, 0x08, 0x41, 0x04, 0x20, 0x20, 0x84, 0x30, 0x42, 0x18, 0x21, 0x0c, 0x10,
0x86, 0x08, 0x43, 0x04, 0x21, 0x08, 0x20, 0x0c, 0x10, 0x06, 0x08, 0x03, 0x04, 0x01, 0x82, 0x10, 0xc0, 0x0c, 0x30,
0x84, 0x21, 0x80, 0x12, 0xd7, 0xd5, 0x46, 0xc0, 0x3c, 0x00, 0x03, 0xf8, 0x18, 0xc0, 0x00, 0x82, 0x06, 0x00, 0x00,
0x20, 0x81, 0xa6, 0x00, 0x08, 0x00, 0x2f, 0x84, 0x00, 0x36, 0xc0, 0x01, 0x00, 0x0c, 0x10, 0x00, 0x40, 0x03, 0x08,
0x00, 0x10, 0x40, 0xc3, 0x00, 0x04, 0x10, 0x31, 0x00, 0x01, 0x00, 0x0c, 0x70, 0x00, 0x40, 0x03, 0x2c, 0x00, 0x10,
0x00, 0xcc, 0x00, 0x04, 0x00, 0x34, 0x00, 0x01, 0x00, 0x0d, 0x10, 0x00, 0x40, 0x03, 0x48, 0x00, 0x10, 0x00, 0xd8,
0x00, 0x04, 0x00, 0x36, 0x40, 0x01, 0x00, 0x0e, 0x50, 0x00, 0x41, 0x03, 0x98, 0x00, 0x10, 0x00, 0xe7, 0x00, 0x04,
0x10, 0x3a, 0x00, 0x01, 0x04, 0x0e, 0x90, 0x00, 0x41, 0x03, 0xa8, 0x00, 0x10, 0x00, 0xef, 0x00, 0x04, 0x00, 0x3f,
0xc0, 0x01, 0x04, 0x1c, 0x60, 0x00, 0x41, 0x46, 0x08, 0x20, 0x63, 0x00, 0x02, 0x48, 0x18, 0xc0, 0x00, 0x8a, 0x0e,
0x30, 0x00, 0x20, 0xa0, 0x04, 0x10, 0x51, 0x82, 0x0e, 0x00, 0x00, 0x20, 0x83, 0xa6, 0x00, 0x08, 0x08, 0xc1, 0x04,
0x15, 0x30, 0x81, 0x8c, 0x00, 0x08, 0x20, 0xaf, 0x84, 0x0c, 0x60, 0x00, 0x41, 0x07, 0xfc, 0x00, 0x10, 0x51, 0x82,
0x08, 0x2f, 0xe1, 0x07, 0x18, 0x00, 0x10, 0x41, 0xe9, 0x00, 0x04, 0x14, 0x60, 0x82, 0x0b, 0x48, 0x41, 0xc6, 0x00,
0x04, 0x10, 0x76, 0xc0, 0x01, 0x01, 0x18, 0x20, 0x82, 0xb6, 0x10, 0x31, 0x80, 0x01, 0x04, 0x1d, 0x90, 0x00, 0x40,
0x46, 0x08, 0x00, 0xac, 0x84, 0x0c, 0x60, 0x00, 0x40, 0x07, 0x1c, 0x00, 0x10, 0x11, 0x82, 0x08, 0x28, 0xe1, 0x03,
0x18, 0x00, 0x10, 0x41, 0xc6, 0x00, 0x04, 0x14, 0x40, 0x80, 0x0a, 0x30, 0x41, 0xc4, 0x00, 0x04, 0x00, 0x71, 0x80,
0x01, 0x05, 0x0c, 0x20, 0x82, 0x8c, 0x10, 0x70, 0xc0, 0x01, 0x04, 0x1c, 0x60, 0x00, 0x41, 0x42, 0x08, 0x20, 0xa3,
0x04, 0x1c, 0x20, 0x00, 0x41, 0x07, 0x18, 0x00, 0x10, 0x50, 0x42, 0x00, 0x28, 0xc1, 0x07, 0x04, 0x00, 0x10, 0x01,
0xc0, 0x00, 0x04, 0x14, 0x00, 0x82, 0x06, 0x00, 0x00, 0x24, 0x03, 0xa6, 0x00, 0x08, 0x08, 0x01, 0x04, 0x15, 0x30,
0x81, 0x80, 0x00, 0x08, 0x20, 0xaf, 0x84, 0x0c, 0x00, 0x00, 0x41, 0x07, 0xa4, 0x00, 0x10, 0x10, 0x02, 0x00, 0x2d,
0x21, 0x03, 0x00, 0x00, 0x10, 0x01, 0xe8, 0x00, 0x04, 0x14, 0x00, 0x82, 0x0b, 0x40, 0x41, 0xc0, 0x00, 0x04, 0x10,
0x79, 0xc0, 0x01, 0x05, 0x00, 0x20, 0x82, 0xce, 0x10, 0x70, 0x00, 0x01, 0x04, 0x1e, 0x50, 0x00, 0x41, 0x40, 0x08,
0x20, 0xb2, 0x84, 0x1c, 0x00, 0x00, 0x41, 0x07, 0x6c, 0x00, 0x10, 0x10, 0x02, 0x08, 0x2b, 0x61, 0x03, 0x00, 0x00,
0x10, 0x41, 0xd2, 0x00, 0x04, 0x04, 0x00, 0x80, 0x0a, 0x90, 0x40, 0xc0, 0x00, 0x04, 0x00, 0x74, 0x40, 0x01, 0x01,
0x00, 0x20, 0x02, 0xa2, 0x10, 0x30, 0x00, 0x01, 0x00, 0x1c, 0x70, 0x00, 0x40, 0x40, 0x08, 0x20, 0xa3, 0x84, 0x0c,
0x00, 0x00, 0x41, 0x07, 0x10, 0x00, 0x10, 0x10, 0x02, 0x08, 0x28, 0x81, 0x03, 0x00, 0x00, 0x10, 0x41, 0xc2, 0x00,
0x04, 0x14, 0x00, 0x82, 0x0a, 0x10, 0x41, 0xc0, 0x00, 0x04, 0x10, 0x57, 0xc2, 0x06, 0x98, 0x00, 0x20, 0x03, 0xce,
0x00, 0x08, 0x2a, 0x61, 0x00, 0x16, 0x70, 0x83, 0xa6, 0x00, 0x08, 0x00, 0xf2, 0x80, 0x02, 0x0a, 0x98, 0x40, 0x05,
0x94, 0x20, 0xe9, 0x80, 0x02, 0x00, 0x3a, 0x60, 0x00, 0x80, 0x84, 0x10, 0x41, 0x53, 0x08, 0x18, 0x40, 0x00, 0x82,
0x0a, 0xf8, 0x40, 0xdb, 0x00, 0x04, 0x00, 0x57, 0xc2, 0x06, 0x38, 0x00, 0x20, 0x02, 0xbe, 0x10, 0x30, 0x80, 0x01,
0x04, 0x1e, 0x90, 0x00, 0x40, 0x5b, 0x08, 0x00, 0xb4, 0x84, 0x0d, 0xb0, 0x00, 0x40, 0x07, 0xa0, 0x00, 0x10, 0x16,
0xc2, 0x00, 0x2d, 0x01, 0x03, 0x6c, 0x00, 0x10, 0x01, 0xe5, 0x00, 0x04, 0x05, 0xb0, 0x80, 0x0b, 0x28, 0x40, 0xdb,
0x00, 0x04, 0x00, 0x76, 0xc0, 0x01, 0x01, 0x0c, 0x20, 0x82, 0xb6, 0x10, 0x30, 0xc0, 0x01, 0x04, 0x1d, 0xb0, 0x00,
0x40, 0x42, 0x08, 0x20, 0xad, 0x84, 0x0c, 0x20, 0x00, 0x41, 0x07, 0x6c, 0x00, 0x10, 0x10, 0x42, 0x00, 0x2b, 0x61,
0x03, 0x04, 0x00, 0x10, 0x01, 0xff, 0x00, 0x04, 0x14, 0x10, 0x80, 0x0b, 0xf8, 0x41, 0xc1, 0x00, 0x04, 0x00, 0x74,
0x00, 0x01, 0x01, 0x04, 0x20, 0x02, 0xa0, 0x10, 0x30, 0x40, 0x01, 0x00, 0x1c, 0xb0, 0x00, 0x40, 0x41, 0x08, 0x00,
0xa5, 0x84, 0x0c, 0x10, 0x00, 0x40, 0x07, 0x10, 0x00, 0x10, 0x10, 0x42, 0x00, 0x28, 0x81, 0x03, 0x04, 0x00, 0x10,
0x01, 0xc3, 0x00, 0x04, 0x14, 0x10, 0x80, 0x0a, 0x18, 0x41, 0xc1, 0x00, 0x04, 0x00, 0x7a, 0x40, 0x01, 0x01, 0x08,
0x20, 0x02, 0xd2, 0x10, 0x30, 0x80, 0x01, 0x00, 0x1e, 0x80, 0x00, 0x41, 0x42, 0x08, 0x20, 0xb4, 0x04, 0x1c, 0x20,
0x00, 0x41, 0x07, 0x9c, 0x00, 0x10, 0x50, 0x82, 0x08, 0x2c, 0xe1, 0x07, 0x08, 0x00, 0x10, 0x41, 0xe5, 0x00, 0x04,
0x14, 0x20, 0x82, 0x0b, 0x28, 0x41, 0xc2, 0x00, 0x04, 0x10, 0x74, 0x80, 0x01, 0x01, 0x08, 0x20, 0x02, 0xa4, 0x10,
0x30, 0x80, 0x01, 0x00, 0x1d, 0x10, 0x00, 0x40, 0x42, 0x08, 0x00, 0xa8, 0x84, 0x0c, 0x20, 0x00, 0x40, 0x07, 0x1c,
0x00, 0x10, 0x10, 0x82, 0x08, 0x28, 0xe1, 0x03, 0x08, 0x00, 0x10, 0x41, 0xc4, 0x00, 0x04, 0x04, 0x20, 0x82, 0x0a,
0x20, 0x40, 0xc2, 0x00, 0x04, 0x10, 0x70, 0x80, 0x01, 0x05, 0x08, 0x20, 0x81, 0x84, 0x00, 0x09, 0x21, 0xb2, 0x84,
0x14, 0x60, 0x83, 0x84, 0x00, 0x09, 0x28, 0x01, 0x04, 0x0c, 0x60, 0x10, 0x49, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63,
0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe,
0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff,
0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff,
0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63,
0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe,
0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff,
0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff,
0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63,
0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe,
0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff,
0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff,
0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63,
0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe,
0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff,
0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff,
0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63,
0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe,
0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff,
0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff,
0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63,
0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe,
0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff,
0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff,
0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63,
0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe,
0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff,
0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0x63, 0xff, 0xff, 0xfe, 0xf9, 0xc0, 0x00, 0x01, 0xfb, 0x10,
0x3e, 0x74, 0x00, 0x00, 0x11, 0xf3, 0x80, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x02, 0x3f, 0x8b, 0xa1, 0xe1, 0xe2, 0xf1,
0x70, 0x43, 0xc3, 0x91, 0x78, 0xbc, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x38, 0x87, 0x04, 0x3c, 0x38, 0x43, 0xc3, 0x90,
0xf0, 0xf1, 0x78, 0xbc, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x39, 0x0f, 0x0e, 0x08, 0x78, 0x70, 0x87, 0x87, 0x22,
0xf1, 0x72, 0x2f, 0x17, 0x87, 0x87, 0x8b, 0xc5, 0xc8, 0x78, 0x78, 0x78, 0x70, 0x23, 0xf8, 0xfe, 0x4f, 0xc9, 0xf8,
0xb9, 0x17, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x41, 0x00, 0x00, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00,
0x40, 0x14, 0x04, 0x05, 0x01, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10,
0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00,
0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01,
0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04,
0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x14, 0x04, 0x05, 0x01, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00,
0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x40, 0x10, 0x04, 0x01,
0x00, 0x40, 0x10, 0x04, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x3c, 0x08, 0x02, 0x48, 0x07, 0x8c, 0xbc, 0xd0, 0x30, 0x1a,
0x58, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x4a, 0x5d, 0xe3, 0x80, 0x74, 0x00, 0x2c, 0x10, 0x00, 0xcd, 0x25,
0x08, 0x00, 0x80, 0x01, 0x2c, 0x3f, 0xc0, 0x12, 0x00, 0x90, 0x04, 0x10, 0x04, 0x80, 0x24, 0x01, 0x20, 0x09, 0x00,
0x48, 0x02, 0x40, 0x12, 0x00, 0x90, 0x04, 0x80, 0x24, 0x01, 0x20, 0x09, 0x00, 0x48, 0x02, 0x40, 0x12, 0x00, 0x90,
0x04, 0x80, 0x24, 0x01, 0x20, 0x09, 0x00, 0x48, 0x0a, 0x04, 0x01, 0x20, 0x09, 0x01, 0x40, 0x80, 0x82, 0x40, 0x50,
0x20, 0x20, 0x90, 0x10, 0x48, 0x0a, 0x04, 0x04, 0x12, 0x02, 0x81, 0x01, 0x04, 0x80, 0xa0, 0x40, 0x41, 0x20, 0x28,
0x10, 0x10, 0x48, 0x0a, 0x04, 0x04, 0x12, 0x02, 0x81, 0x01, 0x04, 0x80, 0xa0, 0x40, 0x41, 0x20, 0x28, 0x10, 0x10,
0x48, 0x0a, 0x04, 0x04, 0x12, 0x02, 0x81, 0x00, 0x48, 0x0a, 0x04, 0x04, 0x12, 0x02, 0x09, 0x01, 0x40, 0x80, 0x82,
0x40, 0x50, 0x20, 0x20, 0x90, 0x14, 0x08, 0x08, 0x24, 0x05, 0x02, 0x02, 0x09, 0x01, 0x40, 0x80, 0x82, 0x40, 0x50,
0x20, 0x20, 0x90, 0x14, 0x08, 0x08, 0x24, 0x05, 0x02, 0x02, 0x09, 0x01, 0x40, 0x80, 0x82, 0x40, 0x50, 0x20, 0x20,
0x90, 0x10, 0x48, 0x0a, 0x04, 0x04, 0x12, 0x02, 0x81, 0x01, 0x04, 0x80, 0xa0, 0x40, 0x41, 0x20, 0x20, 0x90, 0x10,
0x48, 0x08, 0x24, 0x05, 0x02, 0x02, 0x09, 0x01, 0x40, 0x80, 0x82, 0x40, 0x50, 0x20, 0x20, 0x90, 0x14, 0x08, 0x08,
0x24, 0x05, 0x02, 0x02, 0x09, 0x01, 0x40, 0x80, 0x82, 0x40, 0x50, 0x20, 0x20, 0x90, 0x14, 0x08, 0x08, 0x24, 0x05,
0x02, 0x02, 0x09, 0x01, 0x40, 0x80, 0x82, 0x40, 0x50, 0x20, 0x20, 0x90, 0x14, 0x08, 0x08, 0x24, 0x05, 0x02, 0x02,
0x09, 0x01, 0x40, 0x80, 0x82, 0x40, 0x50, 0x20, 0x20, 0x90, 0x14, 0x08, 0x08, 0x24, 0x05, 0x02, 0x02, 0x09, 0x01,
0x40, 0x80, 0x82, 0x40, 0x50, 0x20, 0x20, 0x90, 0x14, 0x08, 0x02, 0x40, 0xc0, 0x10, 0x20, 0x04, 0x0c, 0x43, 0x2a,
0x78, 0x18, 0x00, 0x00, 0x40, 0x20, 0x0c, 0x03, 0x82, 0x60, 0xd8, 0x4a, 0x13, 0x85, 0x02, 0x08, 0x98, 0x26, 0x88,
0x00, 0x44, 0x02, 0x0a, 0x18, 0x32, 0xc0, 0x80, 0x00, 0x02, 0x01, 0x80, 0x70, 0x4c, 0x1b, 0x09, 0x42, 0x71, 0x36,
0x90};
cbit_ref bref(rrc_msg, sizeof(rrc_msg));
ul_dcch_msg_s ul_dcch_msg;
ul_dcch_msg.unpack(bref);
TESTASSERT(ul_dcch_msg.msg.type() == ul_dcch_msg_type_c::types::c1);
TESTASSERT(ul_dcch_msg.msg.c1().type() == ul_dcch_msg_type_c::c1_c_::types::ue_cap_info);
// assign to stack-allocated variable
asn1::rrc::ue_cap_info_s ue_cap;
ue_cap = ul_dcch_msg.msg.c1().ue_cap_info();
TESTASSERT(ue_cap.crit_exts.c1().type() == asn1::rrc::ue_cap_info_s::crit_exts_c_::c1_c_::types::ue_cap_info_r8);
TESTASSERT(ue_cap.crit_exts.c1().ue_cap_info_r8().ue_cap_rat_container_list.size() == 1);
TESTASSERT(ue_cap.crit_exts.c1().ue_cap_info_r8().ue_cap_rat_container_list[0].rat_type ==
asn1::rrc::rat_type_e::eutra);
return 0;
}
int main(int argc, char** argv)
{
auto& asn1_logger = srslog::fetch_basic_logger("ASN1", false);
@ -187,11 +675,16 @@ int main(int argc, char** argv)
srsran::mac_pcap pcap;
pcap.open("ul_dcch.pcap");
TESTASSERT(rrc_ue_cap_info_test(&pcap) == 0);
TESTASSERT(rrc_ue_cap_info_pack_buff_size_test(&pcap, 64) == -1);
TESTASSERT(rrc_ue_cap_info_pack_buff_size_test(&pcap, 128) == 0);
#else
// TESTASSERT(rrc_ue_cap_info_test(NULL) == 0);
TESTASSERT(rrc_ue_cap_info_pack_buff_size_test(NULL, 64) == -1);
TESTASSERT(rrc_ue_cap_info_pack_buff_size_test(NULL, 128) == 0);
#endif
TESTASSERT(pack_fail_test() == -1);
TESTASSERT(rrc_nr_test_scg_fail_packing() == SRSRAN_SUCCESS)
TESTASSERT(rrc_ue_cap_information_test() == 0);
#if PCAP
pcap.close();

@ -370,6 +370,24 @@ enable = false
#auto_target_papr = 8
#ema_alpha = 0.0143
# E2 Agent configuration options
#
# ric_ip: IP address of the RIC controller
# ric_port: Port of the RIC controller
# ric_bind_ip: Local IP address to bind for RIC connection
# ric_bind_port: Local port to bind for RIC connection
# max_ric_setup_retries: Maximum amount of retries to setup the RIC connection. If this value is exceeded, an alarm is written to the log. -1 means infinity.
# ric_connect_timer: Connection Retry Timer for RIC connection (seconds)
#####################################################################
[e2_agent]
#enable = false
#ric_ip = 127.0.0.1
#ric_port = 36421
#ric_bind_ip = 127.0.0.1
#ric_bind_port = 36425
#max_ric_setup_retries = -1
#ric_connect_timer = 10
#####################################################################
# Expert configuration options
#

@ -42,6 +42,8 @@
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsgnb/hdr/stack/gnb_stack_nr.h"
#include "srsgnb/hdr/stack/ric/e2_agent.h"
#include "srsgnb/hdr/stack/ric/e2ap_ric_subscription.h"
#include "srsran/common/bcd_helpers.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/interfaces_common.h"
@ -121,6 +123,7 @@ struct all_args_t {
general_args_t general;
phy_args_t phy;
stack_args_t stack;
e2_agent_args_t e2_agent;
gnb_stack_args_t nr_stack;
};
@ -145,6 +148,8 @@ public:
void print_pool();
bool enable_e2_agent(srsenb::e2_interface_metrics* e2_metrics);
// eNodeB metrics interface
bool get_metrics(enb_metrics_t* m) override;
@ -178,6 +183,7 @@ private:
std::unique_ptr<enb_stack_base> nr_stack = nullptr;
std::unique_ptr<srsran::radio_base> radio = nullptr;
std::unique_ptr<enb_phy_base> phy = nullptr;
std::unique_ptr<e2_agent> _e2_agent = nullptr;
// System metrics processor.
srsran::sys_metrics_processor sys_proc;

@ -0,0 +1,51 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
/******************************************************************************
* File: metrics_e2.h
* Description: Metrics class passing to E2 agent.
*****************************************************************************/
#ifndef SRSENB_METRICS_E2_H
#define SRSENB_METRICS_E2_H
#include "srsran/interfaces/e2_metrics_interface.h"
#include "srsran/interfaces/enb_metrics_interface.h"
#include <pthread.h>
#include <queue>
#include <stdint.h>
#include <string>
#define METRICS_BUFFER_SIZE 20
namespace srsenb {
class metrics_e2 : public srsran::metrics_listener<enb_metrics_t>, public e2_interface_metrics
{
public:
metrics_e2(enb_metrics_interface* enb_) : do_print(false) {}
void set_metrics(const enb_metrics_t& m, const uint32_t period_usec) override;
bool pull_metrics(enb_metrics_t* m) override;
void stop() override{};
bool register_e2sm(e2sm* sm) override;
bool unregister_e2sm(e2sm* sm) override;
private:
std::atomic<bool> do_print = {false};
std::queue<enb_metrics_t> metrics_queue;
enb_metrics_interface* enb = nullptr;
std::vector<e2sm*> e2sm_vec;
};
} // namespace srsenb
#endif // SRSENB_METRICS_STDOUT_H

@ -452,11 +452,11 @@ int number_to_enum(EnumType& enum_val, Setting& root)
ss << val;
fprintf(stderr, "Invalid option: %s for enum field \"%s\"\n", ss.str().c_str(), root.getName());
ss.str("");
ss << EnumType((typename EnumType::options)0).to_number();
ss << std::to_string(EnumType((typename EnumType::options)0).to_number());
fprintf(stderr, "Valid options: %s", ss.str().c_str());
for (uint32_t i = 1; i < EnumType::nof_types; i++) {
ss.str("");
ss << EnumType((typename EnumType::options)i).to_number();
ss << std::to_string(EnumType((typename EnumType::options)i).to_number());
fprintf(stderr, ", %s", ss.str().c_str());
}
fprintf(stderr, "\n");

@ -310,6 +310,7 @@ private:
bool have_mtch_stop = false;
std::mutex mtch_mutex;
std::mutex mbsfn_mutex;
std::condition_variable mtch_cvar;
srsran::phy_cfg_mbsfn_t mbsfn = {};
bool sib13_configured = false;

@ -24,6 +24,7 @@
#include "sched.h"
#include "schedulers/sched_base.h"
#include "srsran/adt/circular_buffer.h"
#include "srsran/adt/pool/cached_alloc.h"
#include "srsran/srslog/srslog.h"
@ -140,9 +141,9 @@ private:
const sched_cell_params_t* cc_cfg = nullptr;
sched_ue_list* ue_db = nullptr;
srsran::deque<pending_rar_t> pending_rars;
uint32_t rar_aggr_level = 2;
static const uint32_t PRACH_RAR_OFFSET = 3; // TS 36.321 Sec. 5.1.4
srsran::dyn_circular_buffer<pending_rar_t> pending_rars;
uint32_t rar_aggr_level = 2;
static const uint32_t PRACH_RAR_OFFSET = 3; // TS 36.321 Sec. 5.1.4
};
} // namespace srsenb

@ -149,6 +149,7 @@ public:
struct ue_cfg_t {
struct cc_cfg_t {
bool active = false;
bool ul_disabled = false;
uint32_t enb_cc_idx = 0; ///< eNB CC index
srsran_dl_cfg_t dl_cfg = {};
uint32_t aperiodic_cqi_period = 0; // if 0 is periodic CQI

@ -123,6 +123,7 @@ public:
const gtpu_interface_rrc::bearer_props* props = nullptr);
void rem_gtpu_bearer(uint32_t erab_id);
void fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg);
void clear_pending_nas_info();
const std::map<uint8_t, erab_t>& get_erabs() const { return erabs; }
const asn1::rrc::drb_to_add_mod_list_l& get_established_drbs() const { return current_drbs; }
@ -136,6 +137,9 @@ private:
const rrc_cfg_t* cfg = nullptr;
gtpu_interface_rrc* gtpu = nullptr;
// NAS PDUs being currently sent.
std::vector<uint32_t> erab_ids_with_pending_nas_pdus;
// last cfg
asn1::rrc::drb_to_add_mod_list_l current_drbs;
};

@ -59,6 +59,8 @@ public:
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container,
asn1::s1ap::cause_c& cause);
std::pair<uint16_t, uint32_t> get_source_ue_rnti_and_pci();
private:
// helper methods
bool update_ue_var_meas_cfg(uint32_t src_earfcn,
@ -118,6 +120,8 @@ private:
struct s1_target_ho_st {
asn1::s1ap::cause_c failure_cause;
std::vector<uint32_t> pending_tunnels;
uint16_t src_rnti;
uint32_t src_pci;
};
struct wait_recfg_comp {};
struct s1_source_ho_st : public subfsm_t<s1_source_ho_st> {

@ -262,6 +262,9 @@ private:
void apply_pdcp_srb_updates(const asn1::rrc::rr_cfg_ded_s& pending_rr_cfg);
void apply_pdcp_drb_updates(const asn1::rrc::rr_cfg_ded_s& pending_rr_cfg);
void apply_rlc_rb_updates(const asn1::rrc::rr_cfg_ded_s& pending_rr_cfg);
/// Find UE whose Handover source identity matches the passed arguments.
ue* find_handover_source_ue(uint16_t old_rnti, uint32_t old_pci);
}; // class ue
} // namespace srsenb

@ -20,8 +20,8 @@
*/
#include <map>
#include <unordered_map>
#include <string.h>
#include <unordered_map>
#include "srsenb/hdr/common/common_enb.h"
#include "srsran/adt/bounded_vector.h"
@ -75,8 +75,8 @@ public:
tunnel* fwd_tunnel = nullptr; ///< forward Rx SDUs to this TEID
srsran::move_callback<void()> on_removal;
tunnel() = default;
tunnel(tunnel&&) noexcept = default;
tunnel() = default;
tunnel(tunnel&&) noexcept = default;
tunnel& operator=(tunnel&&) noexcept = default;
~tunnel()
{
@ -101,7 +101,9 @@ public:
};
using ue_bearer_tunnel_list = srsran::bounded_vector<bearer_teid_pair, MAX_TUNNELS_PER_UE>;
explicit gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger);
explicit gtpu_tunnel_manager(srsran::task_sched_handle task_sched_,
srslog::basic_logger& logger,
srsran::srsran_rat_t ran_type_);
void init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_);
bool has_teid(uint32_t teid) const { return tunnels.contains(teid); }
@ -127,13 +129,16 @@ private:
using tunnel_list_t = srsran::static_id_obj_pool<uint32_t, tunnel, SRSENB_MAX_UES * MAX_TUNNELS_PER_UE>;
using tunnel_ctxt_it = typename tunnel_list_t::iterator;
// Used to differentiate whether GTPU is used in NR or LTE context.
srsran::srsran_rat_t ran_type;
srsran::task_sched_handle task_sched;
const gtpu_args_t* gtpu_args = nullptr;
pdcp_interface_gtpu* pdcp = nullptr;
srslog::basic_logger& logger;
std::unordered_map<uint16_t, ue_bearer_tunnel_list> ue_teidin_db;
tunnel_list_t tunnels;
tunnel_list_t tunnels;
};
using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state;
@ -144,6 +149,7 @@ class gtpu final : public gtpu_interface_rrc, public gtpu_interface_pdcp
public:
explicit gtpu(srsran::task_sched_handle task_sched_,
srslog::basic_logger& logger,
srsran::srsran_rat_t ran_type_,
srsran::socket_manager_itf* rx_socket_handler_);
~gtpu();
@ -177,6 +183,9 @@ private:
srsran::socket_manager_itf* rx_socket_handler = nullptr;
srsran::task_queue_handle gtpu_queue;
// Used to differentiate whether GTPU is used in NR or LTE context.
srsran::srsran_rat_t ran_type;
gtpu_args_t args;
std::string gtp_bind_addr;
std::string mme_addr;
@ -190,10 +199,10 @@ private:
public:
explicit m1u_handler(gtpu* gtpu_) : parent(gtpu_), logger(parent->logger) {}
~m1u_handler();
m1u_handler(const m1u_handler&) = delete;
m1u_handler(m1u_handler&&) = delete;
m1u_handler(const m1u_handler&) = delete;
m1u_handler(m1u_handler&&) = delete;
m1u_handler& operator=(const m1u_handler&) = delete;
m1u_handler& operator=(m1u_handler&&) = delete;
m1u_handler& operator=(m1u_handler&&) = delete;
bool init(std::string m1u_multiaddr_, std::string m1u_if_addr_);
void handle_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr);

@ -69,6 +69,7 @@ cell_list =
// min_phr_thres = 0;
// allowed_meas_bw = 6;
// t304 = 2000; // in msec. possible values: 50, 100, 150, 200, 500, 1000, 2000
// tx_gain = 20.0; // in dB. This gain is set by scaling the source signal.
// CA cells
scell_list = (

@ -36,13 +36,13 @@ endif (RPATH)
add_library(enb_cfg_parser STATIC parser.cc enb_cfg_parser.cc)
target_link_libraries(enb_cfg_parser srsran_common srsgnb_rrc_config_utils ${LIBCONFIGPP_LIBRARIES})
add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc)
add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc metrics_e2.cc)
set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_common srsenb_s1ap srsenb_upper srsenb_mac srsenb_rrc srslog system)
set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_gtpu srsran_rlc srsran_pdcp srsran_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog support system)
set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_stack srsgnb_ngap srsgnb_mac srsgnb_rrc)
set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1)
set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_stack srsgnb_ric srsgnb_ngap srsgnb_mac srsgnb_rrc)
set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1 ric_e2)
target_link_libraries(srsenb ${SRSENB_SOURCES}
${SRSRAN_SOURCES}

@ -134,9 +134,8 @@ int enb::init(const all_args_t& args_)
if (tmp_nr_stack) {
nr_stack = std::move(tmp_nr_stack);
}
phy = std::move(tmp_phy);
radio = std::move(tmp_radio);
phy = std::move(tmp_phy);
radio = std::move(tmp_radio);
started = true; // set to true in any case to allow stopping the eNB if an error happened
// Now that everything is setup, log sector start events.
@ -161,6 +160,10 @@ void enb::stop()
{
if (started) {
// tear down in reverse order
if (_e2_agent) {
_e2_agent->stop();
}
if (phy) {
phy->stop();
}
@ -200,6 +203,22 @@ void enb::start_plot()
phy->start_plot();
}
bool enb::enable_e2_agent(srsenb::e2_interface_metrics* e2_metrics)
{
std::unique_ptr<srsenb::e2_agent> tmp_e2_agent = std::unique_ptr<srsenb::e2_agent>(
new srsenb::e2_agent(srslog::fetch_basic_logger("E2_AGENT", log_sink, false), e2_metrics));
if (tmp_e2_agent == nullptr) {
srsran::console("Error creating e2_agent instance.\n");
return SRSRAN_ERROR;
}
if (tmp_e2_agent->init(args.e2_agent)) {
srsran::console("Error initializing e2_agent client.\n");
return SRSRAN_ERROR;
}
_e2_agent = std::move(tmp_e2_agent);
return SRSRAN_SUCCESS;
}
void enb::print_pool()
{
srsran::byte_buffer_pool::get_instance()->print_all_buffers();
@ -268,6 +287,11 @@ void enb::tti_clock()
if (!started) {
return;
}
if (_e2_agent) {
_e2_agent->tic();
}
if (eutra_stack) {
eutra_stack->tti_clock();
}

@ -1514,6 +1514,10 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root)
HANDLEPARSERCODE(parse_meas_report_desc(&cell_cfg.meas_cfg, cellroot));
}
if (cellroot.exists("barred") and cellroot["barred"]) {
cell_cfg.barred = true;
}
if (cellroot.exists("scell_list")) {
HANDLEPARSERCODE(parse_scell_list(cell_cfg, cellroot));
}

@ -44,6 +44,7 @@
#include "srsenb/hdr/enb.h"
#include "srsenb/hdr/metrics_csv.h"
#include "srsenb/hdr/metrics_e2.h"
#include "srsenb/hdr/metrics_json.h"
#include "srsenb/hdr/metrics_stdout.h"
#include "srsran/common/enb_events.h"
@ -236,7 +237,16 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("cfr.auto_target_papr", bpo::value<float>(&args->phy.cfr_args.auto_target_papr)->default_value(args->phy.cfr_args.auto_target_papr), "Signal PAPR target (in dB) in CFR auto modes")
("cfr.ema_alpha", bpo::value<float>(&args->phy.cfr_args.ema_alpha)->default_value(args->phy.cfr_args.ema_alpha), "Alpha coefficient for the power average in auto_ema mode (0 to 1)")
/* Expert section */
/* RIC section */
("e2_agent.enable", bpo::value<bool>(&args->e2_agent.enable)->default_value(false), "Enables the E2 agent")
("e2_agent.ric_ip", bpo::value<string>(&args->e2_agent.ric_ip)->default_value("127.0.0.1"), "RIC IP address")
("e2_agent.ric_port", bpo::value<uint32_t>(&args->e2_agent.ric_port)->default_value(36421), "RIC port")
("e2_agent.ric_bind_ip", bpo::value<string>(&args->e2_agent.ric_bind_ip)->default_value("127.0.0.1"), "Local IP address to bind for RIC connection")
("e2_agent.ric_bind_port", bpo::value<uint32_t>(&args->e2_agent.ric_bind_port)->default_value(36425), "Local port to bind for RIC connection")
("e2_agent.max_ric_setup_retries", bpo::value<int32_t>(&args->e2_agent.max_ric_setup_retries)->default_value(-1), "Max RIC setup retries")
("e2_agent.ric_connect_timer", bpo::value<uint32_t>(&args->e2_agent.ric_connect_timer)->default_value(10), "Connection Retry Timer for RIC connection (seconds)")
/* Expert section */
("expert.metrics_period_secs", bpo::value<float>(&args->general.metrics_period_secs)->default_value(1.0), "Periodicity for metrics in seconds.")
("expert.metrics_csv_enable", bpo::value<bool>(&args->general.metrics_csv_enable)->default_value(false), "Write metrics to CSV file.")
("expert.metrics_csv_filename", bpo::value<string>(&args->general.metrics_csv_filename)->default_value("/tmp/enb_metrics.csv"), "Metrics CSV filename.")
@ -686,6 +696,10 @@ int main(int argc, char* argv[])
if (args.general.report_json_enable) {
metricshub.add_listener(&json_metrics);
}
srsenb::metrics_e2 e2_metrics(enb.get());
if (args.e2_agent.enable) {
metricshub.add_listener(&e2_metrics);
}
// create input thread
std::thread input(&input_loop, &metrics_screen, (enb_command_interface*)enb.get());
@ -694,6 +708,11 @@ int main(int argc, char* argv[])
if (args.gui.enable) {
enb->start_plot();
}
if (args.e2_agent.enable) {
if (enb->enable_e2_agent(&e2_metrics)) {
srslog::fetch_basic_logger("E2_AGENT").error("Failed to enable E2 Agent");
}
}
}
int cnt = 0;
int ts_cnt = 0;

@ -0,0 +1,59 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsenb/hdr/metrics_e2.h"
#include "srsran/phy/utils/vector.h"
using namespace srsenb;
void metrics_e2::set_metrics(const enb_metrics_t& m, const uint32_t period_usec)
{
if (metrics_queue.size() > METRICS_BUFFER_SIZE) {
metrics_queue.pop();
metrics_queue.push(m);
} else {
metrics_queue.push(m);
}
// send new enb metrics to all registered SMs
for (auto sm_ : e2sm_vec) {
sm_->receive_e2_metrics_callback(m);
}
}
bool metrics_e2::register_e2sm(e2sm* sm)
{
e2sm_vec.push_back(sm);
return true;
}
bool metrics_e2::unregister_e2sm(e2sm* sm)
{
auto it = std::find(e2sm_vec.begin(), e2sm_vec.end(), sm);
if (it != e2sm_vec.end()) {
e2sm_vec.erase(it);
return true;
}
return false;
}
bool metrics_e2::pull_metrics(enb_metrics_t* m)
{
if (enb != nullptr) {
if (!metrics_queue.empty()) {
*m = metrics_queue.front();
metrics_queue.pop();
return true;
}
}
return false;
}

@ -69,9 +69,12 @@ bool phy_common::init(const phy_cell_cfg_list_t& cell_list_,
if (!cell_list_lte.empty()) {
ue_db.init(stack, params, cell_list_lte);
}
if (mcch_configured) {
build_mch_table();
build_mcch_table();
{
std::lock_guard<std::mutex> lock(mbsfn_mutex);
if (mcch_configured) {
build_mch_table();
build_mcch_table();
}
}
reset();
@ -177,6 +180,7 @@ void phy_common::set_mch_period_stop(uint32_t stop)
void phy_common::configure_mbsfn(srsran::phy_cfg_mbsfn_t* cfg)
{
std::lock_guard<std::mutex> lock(mbsfn_mutex);
mbsfn = *cfg;
sib13_configured = true;
mcch_configured = true;

@ -45,7 +45,7 @@ enb_stack_lte::enb_stack_lte(srslog::sink& log_sink) :
pdcp(&task_sched, pdcp_logger),
mac(&task_sched, mac_logger),
rlc(rlc_logger),
gtpu(&task_sched, gtpu_logger, &get_rx_io_manager()),
gtpu(&task_sched, gtpu_logger, srsran::srsran_rat_t::lte, &get_rx_io_manager()),
s1ap(&task_sched, s1ap_logger, &get_rx_io_manager()),
rrc(&task_sched, bearers),
mac_pcap(),

@ -139,17 +139,20 @@ void mac::start_pcap_net(srsran::mac_pcap_net* pcap_net_)
int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue)
{
srsran::rwlock_read_guard lock(rwlock);
int ret = -1;
if (check_ue_active(rnti)) {
if (rnti != SRSRAN_MRNTI) {
srsran::rwlock_read_guard lock(rwlock);
ret = scheduler.dl_rlc_buffer_state(rnti, lc_id, tx_queue, retx_queue);
} else {
for (uint32_t i = 0; i < mch.num_mtch_sched; i++) {
if (lc_id == mch.mtch_sched[i].lcid) {
mch.mtch_sched[i].lcid_buffer_size = tx_queue;
task_sched.defer_callback(0, [this, tx_queue, lc_id]() {
srsran::rwlock_read_guard lock(rwlock);
for (uint32_t i = 0; i < mch.num_mtch_sched; i++) {
if (lc_id == mch.mtch_sched[i].lcid) {
mch.mtch_sched[i].lcid_buffer_size = tx_queue;
}
}
}
});
ret = 0;
}
}
@ -604,7 +607,10 @@ void mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx
}
// Trigger scheduler RACH
scheduler.dl_rach_info(enb_cc_idx, rar_info);
if (scheduler.dl_rach_info(enb_cc_idx, rar_info) != SRSRAN_SUCCESS) {
ue_rem(rnti);
return;
}
auto get_pci = [this, enb_cc_idx]() {
srsran::rwlock_read_guard lock(rwlock);
@ -1062,7 +1068,6 @@ void mac::write_mcch(const srsran::sib2_mbms_t* sib2_,
if (!ret) {
logger.info("Failed to allocate rnti=0x%x.for eMBMS", SRSRAN_MRNTI);
}
rrc_h->add_user(SRSRAN_MRNTI, {});
}
// Internal helper function, caller must hold UE DB rwlock

@ -37,7 +37,8 @@ using srsran::tti_point;
bc_sched::bc_sched(const sched_cell_params_t& cfg_, srsenb::rrc_interface_mac* rrc_) :
cc_cfg(&cfg_), rrc(rrc_), logger(srslog::fetch_basic_logger("MAC"))
{}
{
}
void bc_sched::dl_sched(sf_sched* tti_sched)
{
@ -178,8 +179,9 @@ void bc_sched::reset()
*******************************************************/
ra_sched::ra_sched(const sched_cell_params_t& cfg_, sched_ue_list& ue_db_) :
cc_cfg(&cfg_), logger(srslog::fetch_basic_logger("MAC")), ue_db(&ue_db_)
{}
cc_cfg(&cfg_), logger(srslog::fetch_basic_logger("MAC")), ue_db(&ue_db_), pending_rars(16)
{
}
alloc_result ra_sched::allocate_pending_rar(sf_sched* tti_sched, const pending_rar_t& rar, uint32_t& nof_grants_alloc)
{
@ -214,8 +216,10 @@ void ra_sched::dl_sched(sf_sched* tti_sched)
tti_point tti_tx_dl = tti_sched->get_tti_tx_dl();
rar_aggr_level = 2;
for (auto it = pending_rars.begin(); it != pending_rars.end();) {
auto& rar = *it;
for (auto& rar : pending_rars) {
if (rar.msg3_grant.empty()) {
continue;
}
// In case of RAR outside RAR window:
// - if window has passed, discard RAR
@ -232,7 +236,7 @@ void ra_sched::dl_sched(sf_sched* tti_sched)
tti_tx_dl);
srsran::console("%s\n", srsran::to_c_str(str_buffer));
logger.warning("%s", srsran::to_c_str(str_buffer));
it = pending_rars.erase(it);
rar.msg3_grant.clear(); // mark as handled.
continue;
}
return;
@ -248,7 +252,7 @@ void ra_sched::dl_sched(sf_sched* tti_sched)
// - otherwise, erase only Msg3 grants that were allocated, and stop iteration
if (nof_rar_allocs == rar.msg3_grant.size()) {
it = pending_rars.erase(it);
rar.msg3_grant.clear(); // mark as handled.
} else {
std::copy(rar.msg3_grant.begin() + nof_rar_allocs, rar.msg3_grant.end(), rar.msg3_grant.begin());
rar.msg3_grant.resize(rar.msg3_grant.size() - nof_rar_allocs);
@ -261,9 +265,13 @@ void ra_sched::dl_sched(sf_sched* tti_sched)
if (ret != alloc_result::no_cch_space) {
break;
}
++it;
}
}
// Pop elements at the front that have been handled.
while (not pending_rars.empty() and pending_rars.begin()->msg3_grant.empty()) {
pending_rars.pop();
}
}
int ra_sched::dl_rach_info(dl_sched_rar_info_t rar_info)
@ -296,7 +304,11 @@ int ra_sched::dl_rach_info(dl_sched_rar_info_t rar_info)
p.ra_rnti = ra_rnti;
p.prach_tti = tti_point{rar_info.prach_tti};
p.msg3_grant.push_back(rar_info);
pending_rars.push_back(p);
if (not pending_rars.try_push(p)) {
logger.warning("SCHED: Unable to handle RAR ra-rnti=0x%x, as the maximum number of pending RARs has been reached",
ra_rnti);
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}

@ -448,7 +448,7 @@ alloc_result sf_sched::alloc_rar(uint32_t aggr_lvl, const pending_rar_t& rar, rb
rar_alloc.alloc_data.rbg_range = rbgs;
rar_alloc.alloc_data.req_bytes = buf_rar;
rar_allocs.push_back(rar_alloc);
last_msg3_prb += total_ul_nof_prbs * nof_grants;
last_msg3_prb += total_ul_nof_prbs;
return ret;
}

@ -70,7 +70,13 @@ void sched_time_pf::new_tti(sched_ue_list& ue_db, sf_sched* tti_sched)
dl_queue.push(&it->second);
}
if (it->second.ul_h != nullptr) {
ul_queue.push(&it->second);
// Allocate only if UL carrier is enabled
for (auto& i : u.second->get_ue_cfg().supported_cc_list) {
if (i.enb_cc_idx == cc_cfg->enb_cc_idx and not i.ul_disabled) {
ul_queue.push(&it->second);
break;
}
}
}
}
}

@ -130,6 +130,18 @@ void sched_time_rr::sched_ul_newtxs(sched_ue_list& ue_db, sf_sched* tti_sched, s
iter = ue_db.begin(); // wrap around
}
sched_ue& user = *iter->second;
// Allocate only if UL carrier is enabled
bool ul_disabled = false;
for (auto& i : user.get_ue_cfg().supported_cc_list) {
if (i.enb_cc_idx == cc_cfg->enb_cc_idx) {
ul_disabled = i.ul_disabled;
break;
}
}
if (ul_disabled) {
continue;
}
const ul_harq_proc* h = get_ul_newtx_harq(user, tti_sched);
// Check if there is a empty harq
if (h == nullptr) {

@ -466,9 +466,10 @@ void ue_cfg_apply_reconf_complete_updates(ue_cfg_t& ue_cfg,
if (scell.scell_idx_r10 >= ue_cfg.supported_cc_list.size()) {
ue_cfg.supported_cc_list.resize(scell.scell_idx_r10 + 1);
}
auto& mac_scell = ue_cfg.supported_cc_list[scell.scell_idx_r10];
mac_scell.active = true;
mac_scell.enb_cc_idx = ue_cell_list.get_ue_cc_idx(scell.scell_idx_r10)->cell_common->enb_cc_idx;
auto& mac_scell = ue_cfg.supported_cc_list[scell.scell_idx_r10];
mac_scell.active = true;
mac_scell.ul_disabled = !scell.rr_cfg_common_scell_r10.ul_cfg_r10.ul_freq_info_r10.ul_carrier_freq_r10_present;
mac_scell.enb_cc_idx = ue_cell_list.get_ue_cc_idx(scell.scell_idx_r10)->cell_common->enb_cc_idx;
if (scell.rr_cfg_ded_scell_r10_present and scell.rr_cfg_ded_scell_r10.phys_cfg_ded_scell_r10_present and
scell.rr_cfg_ded_scell_r10.phys_cfg_ded_scell_r10.ul_cfg_r10_present) {

@ -960,6 +960,7 @@ void rrc::configure_mbsfn_sibs()
task_sched.defer_task([this, sibs2, sibs13, mcch_t]() mutable {
phy->configure_mbsfn(&sibs2, &sibs13, mcch_t);
mac->write_mcch(&sibs2, &sibs13, &mcch_t, mcch_payload_buffer, current_mcch_length);
add_user(SRSRAN_MRNTI, {});
});
}

@ -423,17 +423,11 @@ void bearer_cfg_handler::rem_gtpu_bearer(uint32_t erab_id)
void bearer_cfg_handler::fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg)
{
// Add space for NAS messages
uint8_t n_nas = erab_info_list.size();
if (n_nas > 0) {
msg->ded_info_nas_list_present = true;
msg->ded_info_nas_list.resize(n_nas);
}
uint32_t idx = 0;
// DRBs have already been configured in GTPU during bearer setup
// Add E-RAB info message for the E-RABs
if (msg->rr_cfg_ded.drb_to_add_mod_list_present) {
erab_ids_with_pending_nas_pdus.clear();
for (const drb_to_add_mod_s& drb : msg->rr_cfg_ded.drb_to_add_mod_list) {
uint32_t lcid = drb_to_lcid((lte_drb)drb.drb_id);
auto erab_it = std::find_if(
@ -443,15 +437,30 @@ void bearer_cfg_handler::fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_
if (info_it != erab_info_list.end()) {
const std::vector<uint8_t>& erab_info = info_it->second;
logger->info(&erab_info[0], erab_info.size(), "connection_reconf erab_info -> nas_info rnti 0x%x", rnti);
msg->ded_info_nas_list[idx].resize(erab_info.size());
memcpy(msg->ded_info_nas_list[idx].data(), &erab_info[0], erab_info.size());
erab_info_list.erase(info_it);
msg->ded_info_nas_list.push_back({});
msg->ded_info_nas_list.back().resize(erab_info.size());
memcpy(msg->ded_info_nas_list.back().data(), &erab_info[0], erab_info.size());
erab_ids_with_pending_nas_pdus.push_back(erab_id);
} else {
logger->debug("Not adding NAS message to connection reconfiguration. E-RAB id %d", erab_id);
logger->warning("Not adding NAS message to connection reconfiguration. E-RAB id %d", erab_id);
}
idx++;
}
msg->ded_info_nas_list_present = msg->ded_info_nas_list.size() > 0;
}
}
void bearer_cfg_handler::clear_pending_nas_info()
{
for (uint32_t erab_id : erab_ids_with_pending_nas_pdus) {
auto info_it = erab_info_list.find(erab_id);
if (info_it == erab_info_list.end()) {
logger->warning("NAS PDU with ERAB id={} went missing", erab_id);
continue;
}
erab_info_list.erase(info_it);
}
erab_ids_with_pending_nas_pdus.clear();
}
} // namespace srsenb

@ -53,6 +53,12 @@ enb_cell_common_list::enb_cell_common_list(const rrc_cfg_t& cfg_) : cfg(cfg_)
// Update DL EARFCN
new_cell->sib1.freq_band_ind = (uint8_t)srsran_band_get_band(new_cell->cell_cfg.dl_earfcn);
if (new_cell->cell_cfg.barred) {
cell_access->cell_barred.value = sib_type1_s::cell_access_related_info_s_::cell_barred_opts::barred;
} else {
cell_access->cell_barred.value = sib_type1_s::cell_access_related_info_s_::cell_barred_opts::not_barred;
}
// Set Cell SIB2
// update PRACH root seq index for this cell
new_cell->sib2 = cfg.sibs[1].sib2();

@ -216,7 +216,8 @@ uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s&
rrc::ue::rrc_mobility::rrc_mobility(rrc::ue* outer_ue) :
base_t(outer_ue->parent->logger), rrc_ue(outer_ue), rrc_enb(outer_ue->parent), logger(outer_ue->parent->logger)
{}
{
}
//! Method to add Mobility Info to a RRC Connection Reconfiguration Message
bool rrc::ue::rrc_mobility::fill_conn_recfg_no_ho_cmd(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg)
@ -593,7 +594,8 @@ bool rrc::ue::rrc_mobility::needs_intraenb_ho(idle_st& s, const ho_meas_report_e
rrc::ue::rrc_mobility::s1_source_ho_st::s1_source_ho_st(rrc_mobility* parent_) :
base_t(parent_), rrc_enb(parent_->rrc_enb), rrc_ue(parent_->rrc_ue), logger(parent_->logger)
{}
{
}
/**
* TS 36.413, Section 8.4.6 - eNB Status Transfer
@ -863,6 +865,9 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev&
rrc_ue->mac_ctrl.handle_target_enb_ho_cmd(recfg_r8, rrc_ue->ue_capabilities);
// Apply PHY updates
rrc_ue->apply_reconf_phy_config(recfg_r8, true);
// Save source UE PCI and RNTI.
get_state<s1_target_ho_st>()->src_rnti = hoprep_r8.as_cfg.source_ue_id.to_number();
get_state<s1_target_ho_st>()->src_pci = hoprep_r8.as_context.reest_info.source_pci;
// Set admitted E-RABs
std::vector<asn1::s1ap::erab_admitted_item_s> admitted_erabs;
@ -1006,7 +1011,7 @@ bool rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s&
rrc_ue->eutra_capabilities.to_json(js);
logger.debug("New rnti=0x%x EUTRA capabilities: %s", rrc_ue->rnti, js.to_string().c_str());
}
rrc_ue->ue_capabilities = srsran::make_rrc_ue_capabilities(rrc_ue->eutra_capabilities);
rrc_ue->ue_capabilities = srsran::make_rrc_ue_capabilities(rrc_ue->eutra_capabilities, *target_cell);
rrc_ue->eutra_capabilities_unpacked = true;
}
}
@ -1187,4 +1192,13 @@ void rrc::ue::rrc_mobility::handle_recfg_complete(intraenb_ho_st& s, const recfg
logger.info("User rnti=0x%x successfully handovered to cell_id=0x%x", rrc_ue->rnti, s.target_cell->cell_cfg.cell_id);
}
std::pair<uint16_t, uint32_t> rrc::ue::rrc_mobility::get_source_ue_rnti_and_pci()
{
if (not is_ho_running() or (not is_in_state<s1_target_ho_st>() and not is_in_state<wait_recfg_comp>())) {
return std::make_pair(SRSRAN_INVALID_RNTI, (uint32_t)0);
}
const s1_target_ho_st* st = get_state<s1_target_ho_st>();
return std::make_pair(st->src_rnti, st->src_pci);
}
} // namespace srsenb

@ -52,7 +52,8 @@ rrc::ue::ue(rrc* outer_rrc, uint16_t rnti_, const sched_interface::ue_cfg_t& sch
bearer_list(rnti_, parent->cfg, outer_rrc->gtpu),
ue_security_cfg(parent->cfg),
mac_ctrl(rnti, ue_cell_list, bearer_list, parent->cfg, parent->mac, *parent->cell_common_list, sched_ue_cfg)
{}
{
}
rrc::ue::~ue() {}
@ -434,7 +435,8 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
std::string rrc::ue::to_string(const activity_timeout_type_t& type)
{
constexpr static const char* options[] = {"Msg3 reception", "UE inactivity", "UE establishment", "UE reestablishment"};
constexpr static const char* options[] = {
"Msg3 reception", "UE inactivity", "UE establishment", "UE reestablishment"};
return srsran::enum_to_text(options, (uint32_t)activity_timeout_type_t::nulltype, (uint32_t)type);
}
@ -649,20 +651,29 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
return;
}
uint16_t old_pci = msg->crit_exts.rrc_conn_reest_request_r8().ue_id.pci;
const enb_cell_common* old_cell = parent->cell_common_list->get_pci(old_pci);
auto old_ue_it = parent->users.find(old_rnti);
// Reject unrecognized rntis, and PCIs that do not belong to eNB
if (old_ue_it == parent->users.end() or old_cell == nullptr or
old_ue_it->second->ue_cell_list.get_enb_cc_idx(old_cell->enb_cc_idx) == nullptr) {
send_connection_reest_rej(procedure_result_code::error_unknown_rnti);
parent->logger.info(
"RRCReestablishmentReject for rnti=0x%x. Cause: no rnti=0x%x context available", rnti, old_rnti);
srsran::console("RRCReestablishmentReject for rnti=0x%x. Cause: no context available\n", rnti);
return;
uint16_t old_pci = msg->crit_exts.rrc_conn_reest_request_r8().ue_id.pci;
ue* old_ue = nullptr;
{
const enb_cell_common* old_cell = parent->cell_common_list->get_pci(old_pci);
auto old_ue_it = parent->users.find(old_rnti);
// Reject unrecognized rntis, and PCIs that do not belong to eNB
if (old_ue_it == parent->users.end() or old_cell == nullptr or
old_ue_it->second->ue_cell_list.get_enb_cc_idx(old_cell->enb_cc_idx) == nullptr) {
// Check if old UE context does not belong to an S1-Handover UE.
old_ue = find_handover_source_ue(old_rnti, old_pci);
if (old_ue == nullptr) {
send_connection_reest_rej(procedure_result_code::error_unknown_rnti);
parent->logger.info(
"RRCReestablishmentReject for rnti=0x%x. Cause: no rnti=0x%x context available", rnti, old_rnti);
srsran::console("RRCReestablishmentReject for rnti=0x%x. Cause: no context available\n", rnti);
return;
}
} else {
old_ue = old_ue_it->second.get();
}
}
ue* old_ue = old_ue_it->second.get();
bool old_ue_supported_endc = old_ue->endc_handler and old_ue->endc_handler->is_endc_supported();
if (not old_ue_supported_endc and req_r8.reest_cause.value == reest_cause_opts::recfg_fail) {
// Reestablishment Reject for ReconfigFailures of LTE-only mode
@ -959,6 +970,9 @@ void rrc::ue::handle_rrc_reconf_complete(rrc_conn_recfg_complete_s* msg, srsran:
// If performing handover, signal its completion
mobility_handler->trigger(*msg);
// Clear pending NAS PDUs
bearer_list.clear_pending_nas_info();
// 2> if the UE has radio link failure or handover failure information available
const auto& complete_r8 = msg->crit_exts.rrc_conn_recfg_complete_r8();
if (complete_r8.non_crit_ext.non_crit_ext.rlf_info_available_r10_present or rlf_info_pending) {
@ -1058,6 +1072,7 @@ int rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg)
{
parent->logger.info("UECapabilityInformation transaction ID: %d", msg->rrc_transaction_id);
ue_cap_info_r8_ies_s* msg_r8 = &msg->crit_exts.c1().ue_cap_info_r8();
const ue_cell_ded* pcell = ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX);
for (uint32_t i = 0; i < msg_r8->ue_cap_rat_container_list.size(); i++) {
if (msg_r8->ue_cap_rat_container_list[i].rat_type != rat_type_e::eutra) {
@ -1076,9 +1091,16 @@ int rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg)
parent->logger.debug("rnti=0x%x EUTRA capabilities: %s", rnti, js.to_string().c_str());
}
eutra_capabilities_unpacked = true;
ue_capabilities = srsran::make_rrc_ue_capabilities(eutra_capabilities);
ue_capabilities = srsran::make_rrc_ue_capabilities(eutra_capabilities, *pcell);
parent->logger.info("UE rnti: 0x%x category: %d", rnti, eutra_capabilities.ue_category);
if (ue_capabilities.support_ca_bands and ue_capabilities.support_ul_ca) {
parent->logger.info("UE rnti: 0x%x supports DL and UL CA with the used bands.", rnti);
} else if (ue_capabilities.support_ca_bands and not ue_capabilities.support_ul_ca) {
parent->logger.info("UE rnti: 0x%x supports DL CA with the used bands (no UL CA).", rnti);
} else {
parent->logger.info("UE rnti: 0x%x does not support CA with the used bands.", rnti);
}
if (endc_handler != nullptr) {
endc_handler->handle_eutra_capabilities(eutra_capabilities);
@ -1274,6 +1296,13 @@ void rrc::ue::update_scells()
parent->logger.info("UE doesn't support CA. Skipping SCell activation");
return;
}
if (not ue_capabilities.support_ca_bands) {
parent->logger.info("UE doesn't support used CA bands. Skipping SCell activation");
return;
}
if (not ue_capabilities.support_ul_ca) {
parent->logger.info("UE supports only DL CA");
}
if (not eutra_capabilities.non_crit_ext_present or not eutra_capabilities.non_crit_ext.non_crit_ext_present or
not eutra_capabilities.non_crit_ext.non_crit_ext.non_crit_ext_present or
not eutra_capabilities.non_crit_ext.non_crit_ext.non_crit_ext.rf_params_v1020_present or
@ -1645,4 +1674,22 @@ int rrc::ue::get_ri(uint32_t m_ri, uint16_t* ri_idx)
return ret;
}
rrc::ue* rrc::ue::find_handover_source_ue(uint16_t old_rnti, uint32_t old_pci)
{
for (auto& ue_pair : parent->users) {
rrc::ue& u = *ue_pair.second;
if (u.mobility_handler != nullptr and u.mobility_handler->is_ho_running()) {
std::pair<uint16_t, uint32_t> src_ctxt = u.mobility_handler->get_source_ue_rnti_and_pci();
if (src_ctxt.first == old_rnti and src_ctxt.second == old_pci) {
parent->logger.info("Found old UE Context RNTI=0x%x,PCI=%d used for Reestablishment. It corresponds to a UE "
"performing handover.",
old_rnti,
old_pci);
return &u;
}
}
}
return nullptr;
}
} // namespace srsenb

@ -435,6 +435,9 @@ void fill_scells_reconf(asn1::rrc::rrc_conn_recfg_r8_ies_s& recfg_r8,
}
}
const ue_cell_ded* pcell = ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX);
const enb_cell_common* pcell_cfg = pcell->cell_common;
scell_to_add_mod_list_r10_l target_scells(ue_cell_list.nof_cells() - 1);
for (size_t ue_cc_idx = 1; ue_cc_idx < ue_cell_list.nof_cells(); ++ue_cc_idx) {
const ue_cell_ded& scell = *ue_cell_list.get_ue_cc_idx(ue_cc_idx);
@ -456,18 +459,29 @@ void fill_scells_reconf(asn1::rrc::rrc_conn_recfg_r8_ies_s& recfg_r8,
nonul_cfg.phich_cfg_r10 = scell_cfg.mib.phich_cfg;
nonul_cfg.pdsch_cfg_common_r10 = cc_cfg_sib.pdsch_cfg_common;
// RadioResourceConfigCommonSCell-r10::ul-Configuration-r10
asn1cell.rr_cfg_common_scell_r10.ul_cfg_r10_present = true;
auto& ul_cfg = asn1cell.rr_cfg_common_scell_r10.ul_cfg_r10;
ul_cfg.ul_freq_info_r10.ul_carrier_freq_r10_present = true;
ul_cfg.ul_freq_info_r10.ul_carrier_freq_r10 = scell_cfg.cell_cfg.ul_earfcn;
ul_cfg.p_max_r10_present = cell_sib1.p_max_present;
ul_cfg.p_max_r10 = cell_sib1.p_max;
ul_cfg.ul_freq_info_r10.add_spec_emission_scell_r10 = 1;
ul_cfg.ul_pwr_ctrl_common_scell_r10.p0_nominal_pusch_r10 = cc_cfg_sib.ul_pwr_ctrl_common.p0_nominal_pusch;
ul_cfg.ul_pwr_ctrl_common_scell_r10.alpha_r10.value = cc_cfg_sib.ul_pwr_ctrl_common.alpha;
ul_cfg.srs_ul_cfg_common_r10 = cc_cfg_sib.srs_ul_cfg_common;
ul_cfg.ul_cp_len_r10.value = cc_cfg_sib.ul_cp_len.value;
ul_cfg.pusch_cfg_common_r10 = cc_cfg_sib.pusch_cfg_common;
bool ul_allowed = false;
for (const auto& scell_tmp : pcell_cfg->cell_cfg.scell_list) {
if (scell_tmp.cell_id == scell.cell_common->cell_cfg.cell_id) {
ul_allowed = scell_tmp.ul_allowed;
break;
}
}
if (ue_caps.support_ul_ca and ul_allowed) {
asn1cell.rr_cfg_common_scell_r10.ul_cfg_r10_present = true;
auto& ul_cfg = asn1cell.rr_cfg_common_scell_r10.ul_cfg_r10;
ul_cfg.ul_freq_info_r10.ul_carrier_freq_r10_present = true;
ul_cfg.ul_freq_info_r10.ul_carrier_freq_r10 = scell_cfg.cell_cfg.ul_earfcn;
ul_cfg.p_max_r10_present = cell_sib1.p_max_present;
ul_cfg.p_max_r10 = cell_sib1.p_max;
ul_cfg.ul_freq_info_r10.add_spec_emission_scell_r10 = 1;
ul_cfg.ul_pwr_ctrl_common_scell_r10.p0_nominal_pusch_r10 = cc_cfg_sib.ul_pwr_ctrl_common.p0_nominal_pusch;
ul_cfg.ul_pwr_ctrl_common_scell_r10.alpha_r10.value = cc_cfg_sib.ul_pwr_ctrl_common.alpha;
ul_cfg.srs_ul_cfg_common_r10 = cc_cfg_sib.srs_ul_cfg_common;
ul_cfg.ul_cp_len_r10.value = cc_cfg_sib.ul_cp_len.value;
ul_cfg.pusch_cfg_common_r10 = cc_cfg_sib.pusch_cfg_common;
} else {
asn1cell.rr_cfg_common_scell_r10.ul_cfg_r10_present = false;
}
// RadioResourceConfigDedicatedSCell-r10
asn1cell.rr_cfg_ded_scell_r10_present = true;
asn1cell.rr_cfg_ded_scell_r10.phys_cfg_ded_scell_r10_present = true;

@ -21,6 +21,7 @@
#include "srsran/upper/gtpu.h"
#include "srsenb/hdr/stack/upper/gtpu.h"
#include "srsran/common/common_nr.h"
#include "srsran/common/network_utils.h"
#include "srsran/common/standard_streams.h"
#include "srsran/common/string_helpers.h"
@ -41,9 +42,12 @@ namespace srsenb {
#define TEID_IN_FMT "TEID In=0x%x"
#define TEID_OUT_FMT "TEID Out=0x%x"
gtpu_tunnel_manager::gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) :
logger(logger), task_sched(task_sched_), tunnels(1)
{}
gtpu_tunnel_manager::gtpu_tunnel_manager(srsran::task_sched_handle task_sched_,
srslog::basic_logger& logger,
srsran::srsran_rat_t ran_type_) :
logger(logger), ran_type(ran_type_), task_sched(task_sched_), tunnels(1)
{
}
void gtpu_tunnel_manager::init(const gtpu_args_t& args, pdcp_interface_gtpu* pdcp_)
{
@ -66,10 +70,14 @@ gtpu_tunnel_manager::ue_bearer_tunnel_list* gtpu_tunnel_manager::find_rnti_tunne
srsran::span<gtpu_tunnel_manager::bearer_teid_pair>
gtpu_tunnel_manager::find_rnti_bearer_tunnels(uint16_t rnti, uint32_t eps_bearer_id)
{
if (not is_lte_rb(eps_bearer_id)) {
if (ran_type == srsran::srsran_rat_t::lte and not is_eps_bearer_id(eps_bearer_id)) {
logger.warning("Searching for bearer with invalid eps-BearerID=%d", eps_bearer_id);
return {};
}
if (ran_type == srsran::srsran_rat_t::nr and not is_nr_pdu_session_id(eps_bearer_id)) {
logger.warning("Searching for bearer with invalid PDU Session Id=%d", eps_bearer_id);
return {};
}
auto* ue_ptr = find_rnti_tunnels(rnti);
if (ue_ptr == nullptr) {
return {};
@ -83,10 +91,14 @@ gtpu_tunnel_manager::find_rnti_bearer_tunnels(uint16_t rnti, uint32_t eps_bearer
const gtpu_tunnel*
gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t eps_bearer_id, uint32_t teidout, uint32_t spgw_addr)
{
if (not is_lte_rb(eps_bearer_id)) {
if (ran_type == srsran::srsran_rat_t::lte and not is_eps_bearer_id(eps_bearer_id)) {
logger.warning("Adding TEID with invalid eps-BearerID=%d", eps_bearer_id);
return nullptr;
}
if (ran_type == srsran::srsran_rat_t::nr and not is_nr_pdu_session_id(eps_bearer_id)) {
logger.warning("Adding TEID with invalid PDU Session Id=%d", eps_bearer_id);
return nullptr;
}
auto ret_pair = tunnels.insert(tunnel());
if (not ret_pair) {
logger.warning("Unable to create new GTPU TEID In");
@ -360,11 +372,14 @@ void gtpu_tunnel_manager::setup_forwarding(uint32_t rx_teid, uint32_t tx_teid)
gtpu::gtpu(srsran::task_sched_handle task_sched_,
srslog::basic_logger& logger,
srsran::srsran_rat_t ran_type_,
srsran::socket_manager_itf* rx_socket_handler_) :
m1u(this),
task_sched(task_sched_),
logger(logger),
tunnels(task_sched_, logger),
ran_type(ran_type_),
tunnels(task_sched_, logger, ran_type),
rx_socket_handler(rx_socket_handler_)
{
gtpu_queue = task_sched.make_task_queue();

@ -167,7 +167,7 @@ void test_gtpu_tunnel_manager()
srsran::task_scheduler task_sched;
gtpu_args_t gtpu_args = {};
gtpu_tunnel_manager tunnels(&task_sched, srslog::fetch_basic_logger("GTPU"));
gtpu_tunnel_manager tunnels(&task_sched, srslog::fetch_basic_logger("GTPU"), srsran::srsran_rat_t::lte);
tunnels.init(gtpu_args, nullptr);
TESTASSERT(tunnels.find_tunnel(0) == nullptr);
TESTASSERT(tunnels.find_rnti_bearer_tunnels(0x46, drb1_eps_bearer_id).empty());
@ -244,9 +244,10 @@ int test_gtpu_direct_tunneling(tunnel_test_event event)
logger2.set_hex_dump_max_size(2048);
srsran::task_scheduler task_sched;
dummy_socket_manager senb_rx_sockets, tenb_rx_sockets;
srsenb::gtpu senb_gtpu(&task_sched, logger1, &senb_rx_sockets), tenb_gtpu(&task_sched, logger2, &tenb_rx_sockets);
pdcp_tester senb_pdcp, tenb_pdcp;
gtpu_args_t gtpu_args;
srsenb::gtpu senb_gtpu(&task_sched, logger1, srsran::srsran_rat_t::lte, &senb_rx_sockets),
tenb_gtpu(&task_sched, logger2, srsran::srsran_rat_t::lte, &tenb_rx_sockets);
pdcp_tester senb_pdcp, tenb_pdcp;
gtpu_args_t gtpu_args;
gtpu_args.gtp_bind_addr = senb_addr_str;
gtpu_args.mme_addr = sgw_addr_str;
gtpu_args.indirect_tunnel_timeout_msec = std::uniform_int_distribution<uint32_t>{500, 2000}(g);
@ -272,7 +273,7 @@ int test_gtpu_direct_tunneling(tunnel_test_event event)
props.flush_before_teidin = tenb_teid_in;
uint32_t addr_in3;
uint32_t dl_tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1_bearer_id, senb_addr, 0, addr_in3, &props).value();
props = {};
props = {};
props.forward_from_teidin_present = true;
props.forward_from_teidin = senb_teid_in;
uint32_t addr_in4;

@ -211,7 +211,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
exit(1);
}
// Concert hex strings
// Convert hex strings
{
std::stringstream sstr;
sstr << std::hex << vm["mme.mme_group"].as<std::string>();
@ -224,16 +224,8 @@ void parse_args(all_args_t* args, int argc, char* argv[])
sstr >> tmp;
args->mme_args.s1ap_args.mme_code = tmp;
}
{
std::stringstream sstr;
sstr << std::hex << vm["mme.tac"].as<std::string>();
sstr >> args->mme_args.s1ap_args.tac;
}
{
std::stringstream sstr;
sstr << std::hex << vm["mme.lac"].as<std::string>();
sstr >> args->mme_args.s1ap_args.lac;
}
args->mme_args.s1ap_args.tac = std::stoi(vm["mme.tac"].as<std::string>(), nullptr, 0);
args->mme_args.s1ap_args.lac = std::stoi(vm["mme.lac"].as<std::string>(), nullptr, 0);
// Convert MCC/MNC strings
if (!srsran::string_to_mcc(mcc, &args->mme_args.s1ap_args.mcc)) {

@ -183,13 +183,13 @@ bool mme_gtpc::send_create_session_request(uint64_t imsi)
}
// Save RX Control TEID
m_mme_ctr_teid_to_imsi.insert(std::pair<uint32_t, uint64_t>(cs_req->sender_f_teid.teid, imsi));
m_mme_ctr_teid_to_imsi.emplace(cs_req->sender_f_teid.teid, imsi);
// Save GTP-C context
gtpc_ctx_t gtpc_ctx;
std::memset(&gtpc_ctx, 0, sizeof(gtpc_ctx_t));
gtpc_ctx.mme_ctr_fteid = cs_req->sender_f_teid;
m_imsi_to_gtpc_ctx.insert(std::pair<uint64_t, gtpc_ctx_t>(imsi, gtpc_ctx));
m_imsi_to_gtpc_ctx.emplace(imsi, gtpc_ctx);
// Send msg to SPGW
send_s11_pdu(cs_req_pdu);

@ -317,9 +317,9 @@ void s1ap::add_new_enb_ctx(const enb_ctx_t& enb_ctx, const struct sctp_sndrcvinf
std::set<uint32_t> ue_set;
enb_ctx_t* enb_ptr = new enb_ctx_t;
*enb_ptr = enb_ctx;
m_active_enbs.insert(std::pair<uint16_t, enb_ctx_t*>(enb_ptr->enb_id, enb_ptr));
m_sctp_to_enb_id.insert(std::pair<int32_t, uint16_t>(enb_sri->sinfo_assoc_id, enb_ptr->enb_id));
m_enb_assoc_to_ue_ids.insert(std::pair<int32_t, std::set<uint32_t> >(enb_sri->sinfo_assoc_id, ue_set));
m_active_enbs.emplace(enb_ptr->enb_id, enb_ptr);
m_sctp_to_enb_id.emplace(enb_sri->sinfo_assoc_id, enb_ptr->enb_id);
m_enb_assoc_to_ue_ids.emplace(enb_sri->sinfo_assoc_id, ue_set);
}
enb_ctx_t* s1ap::find_enb_ctx(uint16_t enb_id)
@ -371,7 +371,7 @@ bool s1ap::add_nas_ctx_to_imsi_map(nas* nas_ctx)
return false;
}
}
m_imsi_to_nas_ctx.insert(std::pair<uint64_t, nas*>(nas_ctx->m_emm_ctx.imsi, nas_ctx));
m_imsi_to_nas_ctx.emplace(nas_ctx->m_emm_ctx.imsi, nas_ctx);
m_logger.debug("Saved UE context corresponding to IMSI %015" PRIu64 "", nas_ctx->m_emm_ctx.imsi);
return true;
}
@ -394,7 +394,7 @@ bool s1ap::add_nas_ctx_to_mme_ue_s1ap_id_map(nas* nas_ctx)
return false;
}
}
m_mme_ue_s1ap_id_to_nas_ctx.insert(std::pair<uint32_t, nas*>(nas_ctx->m_ecm_ctx.mme_ue_s1ap_id, nas_ctx));
m_mme_ue_s1ap_id_to_nas_ctx.emplace(nas_ctx->m_ecm_ctx.mme_ue_s1ap_id, nas_ctx);
m_logger.debug("Saved UE context corresponding to MME UE S1AP Id %d", nas_ctx->m_ecm_ctx.mme_ue_s1ap_id);
return true;
}
@ -560,7 +560,7 @@ uint32_t s1ap::allocate_m_tmsi(uint64_t imsi)
uint32_t m_tmsi = m_next_m_tmsi;
m_next_m_tmsi = (m_next_m_tmsi + 1) % UINT32_MAX;
m_tmsi_to_imsi.insert(std::pair<uint32_t, uint64_t>(m_tmsi, imsi));
m_tmsi_to_imsi.emplace(m_tmsi, imsi);
m_logger.debug("Allocated M-TMSI 0x%x to IMSI %015" PRIu64 ",", m_tmsi, imsi);
return m_tmsi;
}

@ -464,8 +464,8 @@ spgw_tunnel_ctx_t* spgw::gtpc::create_gtpc_ctx(const struct srsran::gtpc_create_
tunnel_ctx->dw_ctrl_fteid.ipv4 = cs_req.sender_f_teid.ipv4;
std::memset(&tunnel_ctx->dw_user_fteid, 0, sizeof(srsran::gtp_fteid_t));
m_teid_to_tunnel_ctx.insert(std::pair<uint32_t, spgw_tunnel_ctx_t*>(spgw_uplink_ctrl_teid, tunnel_ctx));
m_imsi_to_ctr_teid.insert(std::pair<uint64_t, uint32_t>(cs_req.imsi, spgw_uplink_ctrl_teid));
m_teid_to_tunnel_ctx.emplace(spgw_uplink_ctrl_teid, tunnel_ctx);
m_imsi_to_ctr_teid.emplace(cs_req.imsi, spgw_uplink_ctrl_teid);
return tunnel_ctx;
}

@ -73,8 +73,6 @@ private:
pdu_session_list_t pdu_session_list;
srslog::basic_logger& logger;
std::map<uint32_t, uint32_t> next_lcid_list; // Map RNTI to next LCID to be allocated
int add_gtpu_bearer(uint16_t rnti,
uint32_t pdu_session_id,
uint32_t teid_out,

@ -0,0 +1,134 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#ifndef E2_AGENT_H
#define E2_AGENT_H
#include "srsgnb/hdr/stack/ric/e2ap.h"
#include "srsran/common/network_utils.h"
#include "srsran/common/stack_procedure.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/common/threads.h"
#include "srsran/interfaces/e2_metrics_interface.h"
#include "srsran/srsran.h"
static const int e2ap_ppid = 70;
enum e2_msg_type_t {
E2_SETUP_REQUEST,
E2_SUB_RESPONSE,
E2_SUB_DEL_RESPONSE,
E2_INDICATION,
E2_RESET,
E2_RESET_RESPONSE
};
struct e2_agent_args_t {
bool enable;
std::string ric_ip;
uint32_t ric_port;
std::string ric_bind_ip;
uint32_t ric_bind_port;
int32_t max_ric_setup_retries;
uint32_t ric_connect_timer;
};
namespace srsenb {
class e2_agent : public srsran::thread
{
public:
e2_agent(srslog::basic_logger& logger, srsenb::e2_interface_metrics* _gnb_metrics);
~e2_agent() = default;
// Initiate and Stop
bool init(e2_agent_args_t args);
void stop();
void run_thread();
void tic();
bool is_ric_connected();
// Send messages to RIC
bool send_sctp(srsran::unique_byte_buffer_t& buf);
bool send_e2_msg(e2_msg_type_t msg_type);
bool queue_send_e2ap_pdu(e2_ap_pdu_c send_pdu);
bool send_e2ap_pdu(e2_ap_pdu_c send_pdu);
bool send_reset_response();
// Handle messages received from RIC
bool
handle_ric_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags);
bool handle_e2_rx_pdu(srsran::byte_buffer_t* pdu);
bool handle_e2_init_msg(asn1::e2ap::init_msg_s& init_msg);
bool handle_e2_successful_outcome(asn1::e2ap::successful_outcome_s& successful_outcome);
bool handle_e2_unsuccessful_outcome(asn1::e2ap::unsuccessful_outcome_s& unsuccessful_outcome);
bool handle_e2_setup_response(e2setup_resp_s setup_response);
bool handle_ric_subscription_request(ricsubscription_request_s ric_subscription_request);
bool handle_ric_subscription_delete_request(ricsubscription_delete_request_s ricsubscription_delete_request);
bool handle_subscription_modification_request(uint32_t ric_subscription_modification_request);
bool handle_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm);
bool handle_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse);
bool handle_reset_response(reset_resp_s& reset_response);
bool handle_reset_request(reset_request_s& reset_request);
private:
bool connect_ric();
bool setup_e2();
e2_agent_args_t _args;
srsran::task_scheduler task_sched;
srsran::task_queue_handle ric_rece_task_queue;
srsran::unique_socket ric_socket;
srsran::socket_manager rx_sockets;
srslog::basic_logger& logger;
struct sockaddr_in ric_addr = {}; // RIC address
bool running = false;
bool ric_connected = false;
srsran::unique_timer ric_connect_timer;
srsran::unique_timer e2_setup_timeout;
srsenb::e2_interface_metrics* gnb_metrics = nullptr;
e2ap e2ap_;
// procedures
class e2_setup_proc_t
{
public:
struct e2setupresult {
bool success = false;
enum class cause_t { timeout, failure } cause;
};
struct e2connectresult {
bool success = false;
};
explicit e2_setup_proc_t(e2_agent* e2_agent_) : e2_agent_ptr(e2_agent_) {}
srsran::proc_outcome_t init();
srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; }
srsran::proc_outcome_t react(const e2connectresult& event);
srsran::proc_outcome_t react(const e2setupresult& event);
void then(const srsran::proc_state_t& result);
const char* name() const { return "RIC Connection"; }
uint16_t connect_count = 0;
private:
srsran::proc_outcome_t start_ric_connection();
e2_agent* e2_agent_ptr = nullptr;
};
srsran::proc_t<e2_setup_proc_t> e2_setup_proc;
};
} // namespace srsenb
#endif /* E2_AGENT_H */

@ -0,0 +1,122 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#include "e2sm_kpm.h"
#include "srsran/asn1/e2ap.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/common/timers.h"
#include "srsran/interfaces/e2_metrics_interface.h"
#include "srsran/interfaces/enb_metrics_interface.h"
#include "srsran/srsran.h"
#ifndef RIC_E2AP_H
#define RIC_E2AP_H
namespace srsenb {
using namespace asn1::e2ap;
using namespace asn1::e2sm_kpm;
typedef struct {
uint16_t ric_id;
uint16_t plmn_id;
} global_ric_id_t;
typedef struct {
e2node_component_interface_type_e interface_type;
std::string amf_name;
std::string request_part;
std::string response_part;
} e2_node_component_t;
typedef struct {
uint32_t ric_requestor_id;
uint32_t ric_instance_id;
uint32_t ra_nfunction_id;
std::vector<uint32_t> admitted_actions;
std::vector<uint32_t> not_admitted_actions;
} ric_subscription_reponse_t;
class e2_agent;
class e2ap
{
public:
e2ap(srslog::basic_logger& logger,
e2_agent* _e2_agent,
srsenb::e2_interface_metrics* _gnb_metrics,
srsran::task_scheduler* _task_sched_ptr);
~e2ap();
e2_ap_pdu_c generate_setup_request();
int process_setup_response(e2setup_resp_s setup_response);
int process_setup_failure();
int process_subscription_request(ricsubscription_request_s ric_subscription_request);
int process_subscription_delete_request(ricsubscription_delete_request_s ricsubscription_delete_request);
int process_subscription_modification_request(uint32_t ric_subscription_modification_request);
int process_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm);
int process_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse);
e2_ap_pdu_c generate_subscription_response(ric_subscription_reponse_t ric_subscription_reponse);
e2_ap_pdu_c generate_subscription_failure(ric_subscription_reponse_t ric_subscription_reponse);
e2_ap_pdu_c generate_subscription_delete_response(ric_subscription_reponse_t ric_subscription_reponse);
e2_ap_pdu_c generate_subscription_delete_failure(ric_subscription_reponse_t ric_subscription_reponse);
e2_ap_pdu_c generate_subscription_delete_required(ric_subscription_reponse_t ric_subscription_reponse);
e2_ap_pdu_c generate_subscription_modification_response();
e2_ap_pdu_c generate_subscription_modification_failure();
e2_ap_pdu_c generate_subscription_modification_required();
e2_ap_pdu_c generate_indication(ric_indication_t& ric_indication);
e2_ap_pdu_c generate_reset_request();
e2_ap_pdu_c generate_reset_response();
int process_reset_request(reset_request_s reset_request);
int process_reset_response(reset_resp_s reset_response);
int process_e2_setup_failure(e2setup_fail_s e2setup_failure);
int process_ric_service_update_failure(ricservice_upd_fail_s ric_service_update_failure);
int process_e2_node_config_update_failure(e2node_cfg_upd_fail_s e2node_config_update_failure);
int process_e2_removal_failure(e2_removal_fail_s e2_remove_failure);
bool queue_send_e2ap_pdu(e2_ap_pdu_c e2ap_pdu);
int get_reset_id();
bool get_func_desc(uint32_t ran_func_id, RANfunction_description& fdesc);
bool send_setup_request() { return !e2_established && pending_e2_setup; }
class ric_subscription;
private:
srslog::basic_logger& logger;
e2_agent* _e2_agent;
e2sm_kpm e2sm_;
bool e2_established = false;
srsran::unique_timer e2_procedure_timeout;
bool pending_e2_setup = true;
bool pending_e2_node_config_update = false;
bool pending_ric_service_update = false;
bool pending_e2_removal = false;
int setup_procedure_transaction_id = 0;
uint64_t plmn_id = 0x05f510;
uint64_t gnb_id = 1;
global_ric_id_t global_ric_id = {};
std::map<uint32_t, RANfunction_description> ran_functions;
srsenb::e2_interface_metrics* gnb_metrics = nullptr;
srsran::task_scheduler* task_sched_ptr = nullptr;
bool reset_response_received = false;
int reset_transaction_id = 1;
cause_c reset_cause = cause_c();
int reset_id = 1;
std::vector<std::unique_ptr<ric_subscription> > active_subscriptions;
};
} // namespace srsenb
#endif /* RIC_E2AP_H */

@ -0,0 +1,65 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#ifndef SRSRAN_E2AP_RIC_SUBSCRIPTION_H
#define SRSRAN_E2AP_RIC_SUBSCRIPTION_H
#include "e2ap.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/common/threads.h"
#include "srsran/srsran.h"
namespace srsenb {
class e2ap::ric_subscription
{
public:
ric_subscription(e2ap* e2ap, ricsubscription_request_s ric_subscription_request);
virtual ~ric_subscription() { parent = nullptr; };
uint32_t get_ric_requestor_id() { return ric_requestor_id; };
uint32_t get_ric_instance_id() { return ric_instance_id; };
bool is_initialized() { return initialized; };
void start_subscription();
void delete_subscription();
bool process_subscription_modification_request(uint32_t ric_subscription_modification_request);
bool process_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm);
bool process_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse);
private:
void _send_subscription_response();
void _send_subscription_failure();
void _send_ric_indication();
uint32_t _generate_ric_indication_sn();
e2ap* parent = nullptr;
bool initialized = false;
uint32_t ric_requestor_id;
uint32_t ric_instance_id;
uint16_t ra_nfunction_id;
e2sm* sm_ptr = nullptr;
uint32_t reporting_period = 0; // ms
srsran::unique_timer reporting_timer; // for RIC indication reporting
std::vector<E2AP_RIC_action_t> admitted_actions;
std::vector<uint32_t> not_admitted_actions;
uint32_t _ric_indication_sn_gen = 0;
};
} // namespace srsenb
#endif // SRSRAN_E2AP_RIC_SUBSCRIPTION_H

@ -0,0 +1,107 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#include "srsran/asn1/e2ap.h"
#include "srsran/common/byte_buffer.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/interfaces/enb_metrics_interface.h"
#include "srsran/srsran.h"
#ifndef SRSRAN_E2SM_H
#define SRSRAN_E2SM_H
using namespace asn1::e2ap;
using namespace srsenb;
struct RANfunction_description;
typedef struct {
enum e2sm_event_trigger_type_t { E2SM_REPORT, E2SM_INSERT, E2SM_POLICY, UNKNOWN_TRIGGER };
e2sm_event_trigger_type_t type;
uint64_t report_period;
} RIC_event_trigger_definition_t;
typedef struct {
uint16_t ric_action_id;
ri_caction_type_e ric_action_type;
uint32_t sm_local_ric_action_id;
} E2AP_RIC_action_t;
typedef struct {
uint32_t ric_requestor_id;
uint32_t ric_instance_id;
uint32_t ra_nfunction_id;
uint32_t ri_caction_id;
bool ri_indication_sn_present;
uint32_t ri_indication_sn;
ri_cind_type_e indication_type;
srsran::unique_byte_buffer_t ri_cind_hdr;
srsran::unique_byte_buffer_t ri_cind_msg;
} ric_indication_t;
class e2sm
{
public:
e2sm();
e2sm(std::string short_name,
std::string oid,
std::string func_description,
uint32_t revision,
srsran::task_scheduler* _task_sched_ptr) :
_short_name(short_name),
_oid(oid),
_func_description(func_description),
_revision(revision),
task_sched_ptr(_task_sched_ptr){};
virtual ~e2sm() = default;
std::string get_short_name() { return _short_name; };
std::string get_oid() { return _oid; };
std::string get_func_description() { return _func_description; };
uint32_t get_revision() { return _revision; };
virtual bool generate_ran_function_description(RANfunction_description& desc, ra_nfunction_item_s& ran_func) = 0;
virtual bool process_ric_event_trigger_definition(ricsubscription_request_s subscription_request,
RIC_event_trigger_definition_t& event_def) = 0;
virtual bool process_ric_action_definition(ri_caction_to_be_setup_item_s ric_action,
E2AP_RIC_action_t& action_entry) = 0;
virtual bool remove_ric_action_definition(E2AP_RIC_action_t& action_entry) = 0;
virtual bool generate_ric_indication_content(E2AP_RIC_action_t& action_entry, ric_indication_t& ric_indication) = 0;
virtual void receive_e2_metrics_callback(const enb_metrics_t& m) = 0;
protected:
uint32_t _get_local_action_id() { return _registered_action_id_gen; };
uint32_t _generate_new_local_action_id() { return _registered_action_id_gen++; };
srsran::task_scheduler* task_sched_ptr = nullptr;
private:
const std::string _short_name;
const std::string _oid;
const std::string _func_description;
const uint32_t _revision = 0;
uint32_t _registered_action_id_gen = 1000;
};
struct RANfunction_description {
bool accepted = false;
int function_instance = 0;
e2sm* sm_ptr = nullptr;
std::string function_shortname;
std::string function_e2_sm_oid;
std::string function_desc;
};
#endif // SRSRAN_E2SM_H

@ -0,0 +1,76 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#include "e2sm.h"
#include "e2sm_kpm_common.h"
#include "srsran/asn1/e2ap.h"
#include "srsran/asn1/e2sm.h"
#include "srsran/asn1/e2sm_kpm_v2.h"
#include "srsran/srsran.h"
#ifndef RIC_E2SM_KPM_H
#define RIC_E2SM_KPM_H
using namespace asn1::e2ap;
using namespace asn1::e2sm_kpm;
class e2sm_kpm_report_service;
class e2sm_kpm : public e2sm
{
public:
static const std::string short_name;
static const std::string oid;
static const std::string func_description;
static const uint32_t revision;
e2sm_kpm(srslog::basic_logger& logger_, srsran::task_scheduler* _task_sched_ptr);
~e2sm_kpm();
virtual bool generate_ran_function_description(RANfunction_description& desc, ra_nfunction_item_s& ran_func);
virtual bool process_ric_event_trigger_definition(ricsubscription_request_s subscription_request,
RIC_event_trigger_definition_t& event_def);
virtual bool process_ric_action_definition(ri_caction_to_be_setup_item_s ric_action, E2AP_RIC_action_t& action_entry);
virtual bool remove_ric_action_definition(E2AP_RIC_action_t& action_entry);
virtual bool generate_ric_indication_content(E2AP_RIC_action_t& action_entry, ric_indication_t& ric_indication);
virtual void receive_e2_metrics_callback(const enb_metrics_t& m);
friend class e2sm_kpm_report_service;
friend class e2sm_kpm_report_service_style1;
friend class e2sm_kpm_report_service_style2;
friend class e2sm_kpm_report_service_style3;
friend class e2sm_kpm_report_service_style4;
friend class e2sm_kpm_report_service_style5;
private:
bool _generate_indication_header(e2_sm_kpm_ind_hdr_s& hdr, srsran::unique_byte_buffer_t& buf);
bool _generate_indication_message(e2_sm_kpm_ind_msg_s& msg, srsran::unique_byte_buffer_t& buf);
bool _get_meas_definition(std::string meas_name, e2sm_kpm_metric_t& def);
std::vector<std::string> _get_supported_meas(uint32_t level_mask);
bool _collect_meas_value(e2sm_kpm_meas_def_t& meas_value, meas_record_item_c& item);
bool
_extract_integer_type_meas_value(e2sm_kpm_meas_def_t& meas_value, const enb_metrics_t& enb_metrics, uint32_t& value);
bool _extract_real_type_meas_value(e2sm_kpm_meas_def_t& meas_value, const enb_metrics_t& enb_metrics, float& value);
srslog::basic_logger& logger;
std::vector<e2sm_kpm_metric_t> supported_meas_types;
std::map<uint32_t, e2sm_kpm_report_service*> registered_actions_data;
srsran_random_t random_gen;
enb_metrics_t last_enb_metrics;
};
#endif /*E2SM_KPM*/

@ -0,0 +1,72 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#include "srsran/asn1/e2ap.h"
#include "srsran/asn1/e2sm.h"
#include "srsran/asn1/e2sm_kpm_v2.h"
#include "srsran/srsran.h"
#ifndef SRSRAN_E2SM_KPM_COMMON_H
#define SRSRAN_E2SM_KPM_COMMON_H
using namespace asn1::e2ap;
using namespace asn1::e2sm_kpm;
enum e2_metric_data_type_t { INTEGER, REAL };
typedef struct {
std::string name;
bool supported;
e2_metric_data_type_t data_type;
std::string units;
bool min_val_present;
double min_val;
bool max_val_present;
double max_val;
uint32_t supported_labels;
uint32_t supported_scopes;
} e2sm_kpm_metric_t;
// TODO: define all labels and scopes
/* Labels supported for a metric */
enum e2sm_kpm_label_enum {
NO_LABEL = 0x0001,
MIN_LABEL = 0x0002,
MAX_LABEL = 0x0004,
AVG_LABEL = 0x0008,
SUM_LABEL = 0x0010,
UNKNOWN_LABEL = 0x8000
};
std::string e2sm_kpm_label_2_str(e2sm_kpm_label_enum label);
/* Scopes supported for a metric */
enum e2sm_kpm_metric_scope_enum {
ENB_LEVEL = 0x0001,
CELL_LEVEL = 0x0002,
UE_LEVEL = 0x0004,
BEARER_LEVEL = 0x0008,
UNKNOWN_LEVEL = 0xffff
};
typedef struct {
std::string name;
e2sm_kpm_label_enum label;
e2sm_kpm_metric_scope_enum scope;
meas_record_item_c::types data_type;
uint32_t ue_id; // TODO: do we need to use type ueid_c? or we translate to local RNTI?
uint32_t cell_id; // TODO: do we need to use type cgi_c? or we translate to local cell_id?
} e2sm_kpm_meas_def_t;
#endif // SRSRAN_E2SM_KPM_COMMON_H

@ -0,0 +1,65 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#ifndef SRSRAN_E2SM_KPM_METRICS_H
#define SRSRAN_E2SM_KPM_METRICS_H
#include "e2sm_kpm_common.h"
#include "srsran/srsran.h"
// clang-format off
// Measurements defined in 3GPP TS 28.552
std::vector<e2sm_kpm_metric_t> get_e2sm_kpm_28_552_metrics()
{
// TODO: add all metrics from 3GPP TS 28.552
std::vector<e2sm_kpm_metric_t> metrics;
// not supported metrics
metrics.push_back({"RRU.PrbTotDl", false, REAL, "%", true, 0, true, 100, NO_LABEL | AVG_LABEL, CELL_LEVEL | UE_LEVEL });
metrics.push_back({"RRU.PrbTotUl", false, REAL, "%", true, 0, true, 100, NO_LABEL | AVG_LABEL, CELL_LEVEL | UE_LEVEL });
// not supported metrics
metrics.push_back({"RRU.RachPreambleDedMean", false, REAL, "-", false, 0, false, 100, NO_LABEL, CELL_LEVEL | UE_LEVEL });
return metrics;
}
// Measurements defined in 3GPP TS 32.425
std::vector<e2sm_kpm_metric_t> get_e2sm_kpm_34_425_metrics()
{
// TODO: add all metrics from 3GPP TS 32.425
std::vector<e2sm_kpm_metric_t> metrics;
return metrics;
}
// E2SM_KPM O-RAN specific Measurements
std::vector<e2sm_kpm_metric_t> e2sm_kpm_oran_metrics()
{
// TODO: add all E2SM_KPM O-RAN specific Measurements
std::vector<e2sm_kpm_metric_t> metrics;
return metrics;
}
// Custom Measurements
std::vector<e2sm_kpm_metric_t> e2sm_kpm_custom_metrics()
{
std::vector<e2sm_kpm_metric_t> metrics;
// supported metrics
metrics.push_back({"test", true, INTEGER, "", true, 0, true, 100, NO_LABEL, ENB_LEVEL | CELL_LEVEL | UE_LEVEL });
metrics.push_back({"random_int", true, INTEGER, "", true, 0, true, 100, NO_LABEL, CELL_LEVEL });
metrics.push_back({"cpu0_load", true, REAL, "", true, 0, true, 100, NO_LABEL, ENB_LEVEL });
metrics.push_back({"cpu_load", true, REAL, "", true, 0, true, 100, MIN_LABEL|MAX_LABEL|AVG_LABEL, ENB_LEVEL });
// not supported metrics
metrics.push_back({"test123", false, REAL, "", true, 0, true, 100, NO_LABEL, CELL_LEVEL | UE_LEVEL });
return metrics;
}
// clang-format on
#endif // SRSRAN_E2SM_KPM_METRICS_H

@ -0,0 +1,173 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#include "srsgnb/hdr/stack/ric/e2sm_kpm.h"
#include "srsgnb/hdr/stack/ric/e2sm_kpm_common.h"
#include "srsran/asn1/e2ap.h"
#include "srsran/asn1/e2sm.h"
#include "srsran/asn1/e2sm_kpm_v2.h"
#include "srsran/common/timers.h"
#include "srsran/srsran.h"
#ifndef SRSRAN_E2SM_KPM_ACTION_DATA_H
#define SRSRAN_E2SM_KPM_ACTION_DATA_H
using namespace asn1::e2ap;
using namespace asn1::e2sm_kpm;
class e2sm_kpm_report_service
{
public:
e2sm_kpm_report_service() = delete;
e2sm_kpm_report_service(e2sm_kpm* e2sm_kpm, uint16_t action_id, e2_sm_kpm_action_definition_s action_definition);
virtual ~e2sm_kpm_report_service() = default;
virtual bool _initialize_ric_ind_hdr();
virtual bool _initialize_ric_ind_msg() = 0;
virtual bool _collect_meas_data() = 0;
virtual bool is_ric_ind_ready() = 0;
virtual bool clear_collected_data() = 0;
virtual bool _start_meas_collection();
bool stop();
virtual bool _stop_meas_collection();
virtual bool _reschedule_meas_collection();
std::vector<e2sm_kpm_label_enum> _get_present_labels(const meas_info_item_s& action_meas_info_item);
meas_record_item_c::types
_get_meas_data_type(std::string meas_name, e2sm_kpm_label_enum label, meas_record_l& meas_record_list);
e2_sm_kpm_ind_hdr_s& get_ind_hdr() { return ric_ind_header_generic; };
e2_sm_kpm_ind_msg_s& get_ind_msg() { return ric_ind_message_generic; };
e2sm_kpm* parent;
uint16_t action_id;
e2_sm_kpm_action_definition_s action_def_generic;
e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types ind_msg_format;
e2_sm_kpm_ind_hdr_s ric_ind_header_generic;
e2_sm_kpm_ind_msg_s ric_ind_message_generic;
bool cell_global_id_present = false;
cgi_c cell_global_id;
// hdr format 1 in base class, as all types use it
e2_sm_kpm_ind_hdr_format1_s& ric_ind_header;
uint32_t granul_period = 0;
srsran::unique_timer meas_collection_timer; // for measurements collection
};
class e2sm_kpm_report_service_style1 : public e2sm_kpm_report_service
{
public:
e2sm_kpm_report_service_style1(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition);
virtual ~e2sm_kpm_report_service_style1() = default;
static bool process_ric_action_definition(e2sm_kpm* e2sm_kpm, e2_sm_kpm_action_definition_s& action_definition);
virtual bool _initialize_ric_ind_msg();
virtual bool _collect_meas_data();
virtual bool is_ric_ind_ready();
virtual bool clear_collected_data();
private:
meas_data_item_s&
_get_meas_data_item(std::string meas_name, e2sm_kpm_label_enum label, uint32_t ue_id, bool& ref_found);
e2_sm_kpm_action_definition_format1_s& action_def;
e2_sm_kpm_ind_msg_format1_s& ric_ind_message;
};
class e2sm_kpm_report_service_style2 : public e2sm_kpm_report_service
{
public:
e2sm_kpm_report_service_style2(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition);
virtual ~e2sm_kpm_report_service_style2() = default;
static bool process_ric_action_definition(e2sm_kpm* e2sm_kpm, e2_sm_kpm_action_definition_s& action_definition);
virtual bool _initialize_ric_ind_msg();
virtual bool _collect_meas_data();
virtual bool is_ric_ind_ready();
virtual bool clear_collected_data();
private:
e2_sm_kpm_action_definition_format2_s& action_def;
e2_sm_kpm_ind_msg_format1_s& ric_ind_message;
};
class e2sm_kpm_report_service_style3 : public e2sm_kpm_report_service
{
public:
e2sm_kpm_report_service_style3(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition);
virtual ~e2sm_kpm_report_service_style3() = default;
static bool process_ric_action_definition(e2sm_kpm* e2sm_kpm, e2_sm_kpm_action_definition_s& action_definition);
virtual bool _initialize_ric_ind_msg();
virtual bool _collect_meas_data();
virtual bool is_ric_ind_ready();
virtual bool clear_collected_data();
private:
e2_sm_kpm_action_definition_format3_s& action_def;
e2_sm_kpm_ind_msg_format2_s& ric_ind_message;
};
class e2sm_kpm_report_service_style4 : public e2sm_kpm_report_service
{
public:
e2sm_kpm_report_service_style4(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition);
virtual ~e2sm_kpm_report_service_style4() = default;
static bool process_ric_action_definition(e2sm_kpm* e2sm_kpm, e2_sm_kpm_action_definition_s& action_definition);
virtual bool _initialize_ric_ind_msg();
virtual bool _collect_meas_data();
virtual bool is_ric_ind_ready();
virtual bool clear_collected_data();
private:
e2_sm_kpm_action_definition_format4_s& action_def;
e2_sm_kpm_ind_msg_format3_s& ric_ind_message;
};
class e2sm_kpm_report_service_style5 : public e2sm_kpm_report_service
{
public:
e2sm_kpm_report_service_style5(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition);
virtual ~e2sm_kpm_report_service_style5() = default;
static bool process_ric_action_definition(e2sm_kpm* e2sm_kpm, e2_sm_kpm_action_definition_s& action_definition);
virtual bool _initialize_ric_ind_msg();
virtual bool _collect_meas_data();
virtual bool is_ric_ind_ready();
virtual bool clear_collected_data();
private:
e2_sm_kpm_action_definition_format5_s& action_def;
e2_sm_kpm_ind_msg_format3_s& ric_ind_message;
};
#endif // SRSRAN_E2SM_KPM_ACTION_DATA_H

@ -195,7 +195,7 @@ private:
// MAC controller
sched_nr_interface::ue_cfg_t uecfg{};
const uint32_t drb1_lcid = 4;
const uint32_t drb1_lcid = 4;
uint32_t drb1_five_qi = 0; /// selected by 5GC
// Security helper

@ -22,6 +22,7 @@ include_directories(${PROJECT_SOURCE_DIR})
add_subdirectory(mac)
add_subdirectory(ngap)
add_subdirectory(ric)
add_subdirectory(rrc)
add_subdirectory(sdap)

@ -92,7 +92,7 @@ int gnb_stack_nr::init(const gnb_stack_args_t& args_,
if (x2_ == nullptr) {
// SA mode
ngap.reset(new srsenb::ngap(&task_sched, ngap_logger, &srsran::get_rx_io_manager()));
gtpu.reset(new srsenb::gtpu(&task_sched, gtpu_logger, &srsran::get_rx_io_manager()));
gtpu.reset(new srsenb::gtpu(&task_sched, gtpu_logger, srsran::srsran_rat_t::nr, &srsran::get_rx_io_manager()));
gtpu_adapter.reset(new gtpu_pdcp_adapter(gtpu_logger, nullptr, &pdcp, gtpu.get(), *bearer_manager));
}

@ -20,11 +20,13 @@
*/
#include "srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h"
#include "srsran/common/common_nr.h"
namespace srsenb {
ngap_ue_bearer_manager::ngap_ue_bearer_manager(gtpu_interface_rrc* gtpu_, srslog::basic_logger& logger_) :
gtpu(gtpu_), logger(logger_)
{}
{
}
ngap_ue_bearer_manager::~ngap_ue_bearer_manager(){};
int ngap_ue_bearer_manager::add_pdu_session(uint16_t rnti,
@ -47,6 +49,10 @@ int ngap_ue_bearer_manager::add_pdu_session(uint16_t
}
lcid = allocate_lcid(rnti);
if (lcid >= srsran::MAX_NR_NOF_BEARERS) {
logger.error("Adding PDU Session ID=%d to GTPU. No free LCID.", pdu_session_id);
return SRSRAN_ERROR;
}
// TODO: remove lcid and just use pdu_session_id and rnti as id for GTP tunnel
int rtn = add_gtpu_bearer(rnti, pdu_session_id, teid_out, addr_out, tunnel);
@ -71,9 +77,8 @@ int ngap_ue_bearer_manager::reset_pdu_sessions(uint16_t rnti)
{
for (auto iter = pdu_session_list.begin(); iter != pdu_session_list.end(); iter++) {
auto pdu_session_id = iter->first;
rem_gtpu_bearer(pdu_session_id, rnti);
rem_gtpu_bearer(rnti, pdu_session_id);
}
next_lcid_list.erase(rnti);
return true;
}
@ -124,10 +129,20 @@ void ngap_ue_bearer_manager::rem_gtpu_bearer(uint16_t rnti, uint32_t pdu_session
uint8_t ngap_ue_bearer_manager::allocate_lcid(uint32_t rnti)
{
if (next_lcid_list.find(rnti) == next_lcid_list.end()) {
next_lcid_list[rnti] = 4;
if (pdu_session_list.empty()) {
return 4;
}
for (unsigned lcid = 4; lcid < srsran::MAX_NR_NOF_BEARERS; lcid++) {
const auto pdu_session_it =
std::find_if(pdu_session_list.cbegin(),
pdu_session_list.cend(),
[lcid](const std::pair<uint8_t, pdu_session_t>& t) { return t.second.lcid != lcid; });
if (pdu_session_it != pdu_session_list.cend()) {
return lcid;
}
}
return next_lcid_list[rnti]++;
// All LCIDs are used.
return srsran::MAX_NR_NOF_BEARERS;
}
} // namespace srsenb

@ -0,0 +1,13 @@
#
# Copyright 2013-2023 Software Radio Systems Limited
#
# By using this file, you agree to the terms and conditions set
# forth in the LICENSE file which can be found at the top level of
# the distribution.
#
set(SOURCES e2_agent.cc e2ap_ric_subscription.cc e2ap.cc e2sm_kpm_common.cc e2sm_kpm.cc e2sm_kpm_report_service.cc)
add_library(srsgnb_ric STATIC ${SOURCES})
target_link_libraries(srsgnb_ric srsran_asn1 ric_e2)
add_subdirectory(test)

@ -0,0 +1,599 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsgnb/hdr/stack/ric/e2_agent.h"
#include "srsran/asn1/e2ap.h"
#include "srsran/common/standard_streams.h"
using namespace srsenb;
/*********************************************************
* RIC Connection
*********************************************************/
srsran::proc_outcome_t e2_agent::e2_setup_proc_t::init()
{
e2_agent_ptr->logger.info("Starting new RIC connection.");
connect_count++;
return start_ric_connection();
}
srsran::proc_outcome_t e2_agent::e2_setup_proc_t::start_ric_connection()
{
if (not e2_agent_ptr->running) {
e2_agent_ptr->logger.info("E2 Agent is not running anymore.");
return srsran::proc_outcome_t::error;
}
if (e2_agent_ptr->ric_connected) {
e2_agent_ptr->logger.info("E2 Agent is already connected to RIC");
return srsran::proc_outcome_t::success;
}
auto connect_callback = [this]() {
bool connected = e2_agent_ptr->connect_ric();
auto notify_result = [this, connected]() {
e2_setup_proc_t::e2connectresult res;
res.success = connected;
e2_agent_ptr->e2_setup_proc.trigger(res);
};
e2_agent_ptr->task_sched.notify_background_task_result(notify_result);
};
srsran::get_background_workers().push_task(connect_callback);
e2_agent_ptr->logger.debug("Connection to RIC requested.");
return srsran::proc_outcome_t::yield;
}
srsran::proc_outcome_t e2_agent::e2_setup_proc_t::react(const srsenb::e2_agent::e2_setup_proc_t::e2connectresult& event)
{
if (event.success) {
e2_agent_ptr->logger.info("Connected to RIC. Sending setup request.");
e2_agent_ptr->e2_setup_timeout.run();
if (not e2_agent_ptr->setup_e2()) {
e2_agent_ptr->logger.error("E2 setup failed. Exiting...");
srsran::console("E2 setup failed\n");
e2_agent_ptr->running = false;
return srsran::proc_outcome_t::error;
}
e2_agent_ptr->logger.info("E2 setup request sent. Waiting for response.");
return srsran::proc_outcome_t::yield;
}
e2_agent_ptr->logger.info("Could not connected to RIC. Aborting");
return srsran::proc_outcome_t::error;
}
srsran::proc_outcome_t e2_agent::e2_setup_proc_t::react(const srsenb::e2_agent::e2_setup_proc_t::e2setupresult& event)
{
if (e2_agent_ptr->e2_setup_timeout.is_running()) {
e2_agent_ptr->e2_setup_timeout.stop();
}
if (event.success) {
e2_agent_ptr->logger.info("E2 Setup procedure completed successfully");
return srsran::proc_outcome_t::success;
}
e2_agent_ptr->logger.error("E2 Setup failed.");
srsran::console("E2 setup failed\n");
return srsran::proc_outcome_t::error;
}
void e2_agent::e2_setup_proc_t::then(const srsran::proc_state_t& result)
{
if (result.is_error()) {
e2_agent_ptr->logger.info("Failed to initiate RIC connection. Attempting reconnection in %d seconds",
e2_agent_ptr->ric_connect_timer.duration() / 1000);
srsran::console("Failed to initiate RIC connection. Attempting reconnection in %d seconds\n",
e2_agent_ptr->ric_connect_timer.duration() / 1000);
e2_agent_ptr->rx_sockets.remove_socket(e2_agent_ptr->ric_socket.get_socket());
e2_agent_ptr->ric_socket.close();
e2_agent_ptr->logger.info("R2 Agent socket closed.");
e2_agent_ptr->ric_connect_timer.run();
if (e2_agent_ptr->_args.max_ric_setup_retries > 0 && connect_count > e2_agent_ptr->_args.max_ric_setup_retries) {
srsran_terminate("Error connecting to RIC");
}
// Try again with in 10 seconds
} else {
connect_count = 0;
}
}
/*********************************************************
* E2 Agent class
*********************************************************/
e2_agent::e2_agent(srslog::basic_logger& logger, e2_interface_metrics* _gnb_metrics) :
task_sched(),
logger(logger),
rx_sockets(),
thread("E2_AGENT_THREAD"),
e2ap_(logger, this, _gnb_metrics, &task_sched),
e2_setup_proc(this)
{
gnb_metrics = _gnb_metrics;
ric_rece_task_queue = task_sched.make_task_queue();
}
bool e2_agent::init(e2_agent_args_t args)
{
_args = args;
// Setup RIC reconnection timer
ric_connect_timer = task_sched.get_unique_timer();
auto ric_connect_run = [this](uint32_t tid) {
if (e2_setup_proc.is_busy()) {
logger.error("Failed to initiate RIC Setup procedure: procedure is busy.");
}
e2_setup_proc.launch();
};
ric_connect_timer.set(_args.ric_connect_timer * 1000, ric_connect_run);
// Setup timeout
e2_setup_timeout = task_sched.get_unique_timer();
uint32_t ric_setup_timeout_val = 5000;
e2_setup_timeout.set(ric_setup_timeout_val, [this](uint32_t tid) {
e2_setup_proc_t::e2setupresult res;
res.success = false;
res.cause = e2_setup_proc_t::e2setupresult::cause_t::timeout;
e2_setup_proc.trigger(res);
});
start(0);
running = true;
// starting RIC connection
if (not e2_setup_proc.launch()) {
logger.error("Failed to initiate RIC Setup procedure: error launching procedure.");
}
return SRSRAN_SUCCESS;
}
void e2_agent::stop()
{
running = false;
wait_thread_finish();
}
void e2_agent::tic()
{
// get tick every 1ms to advance timers
task_sched.tic();
}
bool e2_agent::is_ric_connected()
{
return ric_connected;
}
bool e2_agent::connect_ric()
{
using namespace srsran::net_utils;
logger.info("Connecting to RIC %s:%d", _args.ric_ip.c_str(), _args.ric_port);
// Open SCTP socket
if (not ric_socket.open_socket(addr_family::ipv4, socket_type::seqpacket, protocol_type::SCTP)) {
return false;
}
// Subscribe to shutdown events
if (not ric_socket.sctp_subscribe_to_events()) {
ric_socket.close();
return false;
}
// Set SRTO_MAX
if (not ric_socket.sctp_set_rto_opts(6000)) {
return false;
}
// Set SCTP init options
if (not ric_socket.sctp_set_init_msg_opts(3, 5000)) {
return false;
}
// Bind socket
if (not ric_socket.bind_addr(_args.ric_bind_ip.c_str(), _args.ric_bind_port)) {
ric_socket.close();
return false;
}
logger.info("SCTP socket opened. fd=%d", ric_socket.fd());
// Connect to the AMF address
if (not ric_socket.connect_to(_args.ric_ip.c_str(), _args.ric_port, &ric_addr)) {
ric_socket.close();
return false;
}
logger.info("SCTP socket connected with RIC. fd=%d", ric_socket.fd());
// Assign a handler to rx RIC packets
auto rx_callback =
[this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags) {
handle_ric_rx_msg(std::move(pdu), from, sri, flags);
};
rx_sockets.add_socket_handler(ric_socket.fd(),
srsran::make_sctp_sdu_handler(logger, ric_rece_task_queue, rx_callback));
logger.info("SCTP socket connected established with RIC");
return true;
}
bool e2_agent::setup_e2()
{
return send_e2_msg(E2_SETUP_REQUEST);
}
void e2_agent::run_thread()
{
while (running) {
task_sched.run_next_task();
}
}
bool e2_agent::send_sctp(srsran::unique_byte_buffer_t& buf)
{
ssize_t ret;
ret = sctp_sendmsg(ric_socket.fd(),
buf->msg,
buf->N_bytes,
(struct sockaddr*)&ric_addr,
sizeof(ric_addr),
htonl(e2ap_ppid),
0,
0,
0,
0);
if (ret == -1) {
printf("failed to send %d bytes\n", buf->N_bytes);
return false;
}
return true;
}
bool e2_agent::send_e2_msg(e2_msg_type_t msg_type)
{
std::string message_name;
e2_ap_pdu_c send_pdu;
switch (msg_type) {
case e2_msg_type_t::E2_SETUP_REQUEST:
send_pdu = e2ap_.generate_setup_request();
message_name = "E2 SETUP REQUEST";
break;
case e2_msg_type_t::E2_RESET:
send_pdu = e2ap_.generate_reset_request();
message_name = "E2 RESET REQUEST";
break;
case e2_msg_type_t::E2_RESET_RESPONSE:
send_pdu = e2ap_.generate_reset_response();
message_name = "E2 RESET RESPONSE";
break;
default:
printf("Unknown E2AP message type\n");
return false;
}
return send_e2ap_pdu(send_pdu);
}
bool e2_agent::queue_send_e2ap_pdu(e2_ap_pdu_c e2ap_pdu)
{
if (not ric_connected) {
logger.error("Aborting sending msg. Cause: RIC is not connected.");
return false;
}
auto send_e2ap_pdu_task = [this, e2ap_pdu]() { send_e2ap_pdu(e2ap_pdu); };
ric_rece_task_queue.push(send_e2ap_pdu_task);
return true;
}
bool e2_agent::send_e2ap_pdu(e2_ap_pdu_c send_pdu)
{
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
if (buf == nullptr) {
// logger.error("Fatal Error: Couldn't allocate buffer for %s.", procedure_name);
return false;
}
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
if (send_pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
logger.error("Failed to pack TX E2 PDU");
return false;
}
buf->N_bytes = bref.distance_bytes();
printf("try to send %d bytes to addr %s \n", buf->N_bytes, inet_ntoa(ric_addr.sin_addr));
if (!send_sctp(buf)) {
logger.error("failed to send");
return false;
}
return true;
}
bool e2_agent::handle_ric_rx_msg(srsran::unique_byte_buffer_t pdu,
const sockaddr_in& from,
const sctp_sndrcvinfo& sri,
int flags)
{
// Handle Notification Case
if (flags & MSG_NOTIFICATION) {
// Received notification
union sctp_notification* notification = (union sctp_notification*)pdu->msg;
logger.info("SCTP Notification %04x", notification->sn_header.sn_type);
bool restart_e2 = false;
if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) {
logger.info("SCTP Association Shutdown. Association: %d", sri.sinfo_assoc_id);
srsran::console("SCTP Association Shutdown. Association: %d\n", sri.sinfo_assoc_id);
restart_e2 = true;
} else if (notification->sn_header.sn_type == SCTP_PEER_ADDR_CHANGE &&
notification->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) {
logger.info("SCTP peer addres unreachable. Association: %d", sri.sinfo_assoc_id);
srsran::console("SCTP peer address unreachable. Association: %d\n", sri.sinfo_assoc_id);
restart_e2 = true;
} else if (notification->sn_header.sn_type == SCTP_REMOTE_ERROR) {
logger.info("SCTP remote error. Association: %d", sri.sinfo_assoc_id);
srsran::console("SCTP remote error. Association: %d\n", sri.sinfo_assoc_id);
restart_e2 = true;
} else if (notification->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
logger.info("SCTP association changed. Association: %d", sri.sinfo_assoc_id);
srsran::console("SCTP association changed. Association: %d\n", sri.sinfo_assoc_id);
}
if (restart_e2) {
logger.info("Restarting E2 connection");
srsran::console("Restarting E2 connection\n");
rx_sockets.remove_socket(ric_socket.get_socket());
ric_socket.close();
}
} else if (pdu->N_bytes == 0) {
logger.error("SCTP return 0 bytes. Closing socket");
ric_socket.close();
}
// Restart RIC connection procedure if we lost connection
if (not ric_socket.is_open()) {
ric_connected = false;
if (e2_setup_proc.is_busy()) {
logger.error("Failed to initiate RIC connection procedure, as it is already running.");
return false;
}
e2_setup_proc.launch();
return false;
}
if ((flags & MSG_NOTIFICATION) == 0 && pdu->N_bytes != 0) {
handle_e2_rx_pdu(pdu.get());
}
return true;
}
bool e2_agent::handle_e2_rx_pdu(srsran::byte_buffer_t* pdu)
{
printf("E2_AGENT: Received %d bytes from RIC\n", pdu->N_bytes);
e2_ap_pdu_c pdu_c;
asn1::cbit_ref bref(pdu->msg, pdu->N_bytes);
if (pdu_c.unpack(bref) != asn1::SRSASN_SUCCESS) {
logger.error("Failed to unpack RX E2 PDU");
return false;
}
if (pdu_c.type().value == e2_ap_pdu_c::types_opts::init_msg) {
logger.info("Received E2AP Init Message");
handle_e2_init_msg(pdu_c.init_msg());
} else if (pdu_c.type().value == e2_ap_pdu_c::types_opts::successful_outcome) {
logger.info("Received E2AP Successful Outcome");
handle_e2_successful_outcome(pdu_c.successful_outcome());
} else if (pdu_c.type().value == e2_ap_pdu_c::types_opts::unsuccessful_outcome) {
logger.info("Received E2AP Unsuccessful Outcome ");
handle_e2_unsuccessful_outcome(pdu_c.unsuccessful_outcome());
} else {
logger.warning("Received E2AP Unknown Message ");
}
return true;
}
bool e2_agent::handle_e2_init_msg(asn1::e2ap::init_msg_s& init_msg)
{
using namespace asn1::e2ap;
if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::ricsubscription_request) {
logger.info("Received E2AP RIC Subscription Request");
handle_ric_subscription_request(init_msg.value.ricsubscription_request());
} else if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::ricsubscription_delete_request) {
logger.info("Received E2AP RIC Subscription Delete Request");
handle_ric_subscription_delete_request(init_msg.value.ricsubscription_delete_request());
} else if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::ri_cctrl_request) {
logger.info("Received E2AP RIC Control Request");
// handle_ri_cctrl_request(init_msg.value.ri_cctrl_request());
} else if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::e2conn_upd) {
logger.info("Received E2AP E2 Connection Update");
//handle_e2conn_upd(init_msg.value.e2conn_upd());
} else if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::reset_request) {
logger.info("Received E2AP E2 Reset Request");
handle_reset_request(init_msg.value.reset_request());
} else if (init_msg.value.type() == e2_ap_elem_procs_o::init_msg_c::types_opts::e2_removal_request) {
logger.info("Received E2AP E2 Removal Request");
//handle_e2_removal_request(init_msg.value.e2_removal_request());
} else {
logger.warning("Received E2AP Unknown Init Message ");
}
// TODO check for different type of RIC generated init messages
// eg. RIC subscription request, RIC Reset request, RIC control request, RIC subscription delete request
return true;
}
bool e2_agent::handle_e2_successful_outcome(asn1::e2ap::successful_outcome_s& successful_outcome)
{
using namespace asn1::e2ap;
if (successful_outcome.value.type() == e2_ap_elem_procs_o::successful_outcome_c::types_opts::e2setup_resp) {
logger.info("Received E2AP E2 Setup Response");
handle_e2_setup_response(successful_outcome.value.e2setup_resp());
} else if (successful_outcome.value.type() ==
e2_ap_elem_procs_o::successful_outcome_c::types_opts::ricsubscription_resp) {
logger.info("Received E2AP RIC Subscription Response");
// handle_ric_subscription_response(successful_outcome.value.ric_subscription());
} else if (successful_outcome.value.type() == e2_ap_elem_procs_o::successful_outcome_c::types_opts::ri_cctrl_ack) {
logger.info("Received E2AP RIC Control acknowlegement \n");
// handle_ric_control_response(successful_outcome.value.ric_control());
} else if (successful_outcome.value.type() ==
e2_ap_elem_procs_o::successful_outcome_c::types_opts::ricservice_upd_ack) {
logger.info("Received E2AP RIC Service Update acknowlegement \n");
// handle_ric_service_update_ack(successful_outcome.value.ric_service_update());
} else if (successful_outcome.value.type() ==
e2_ap_elem_procs_o::successful_outcome_c::types_opts::ricsubscription_delete_resp) {
logger.info("Received E2AP RIC Subscription Delete Response \n");
// handle_ric_subscription_delete_response(successful_outcome.value.ric_subscription_delete());
} else if (successful_outcome.value.type() == e2_ap_elem_procs_o::successful_outcome_c::types_opts::reset_resp) {
logger.info("Received E2AP RIC Reset Response \n");
handle_reset_response(successful_outcome.value.reset_resp());
} else {
logger.info("Received E2AP Unknown Successful Outcome \n");
}
return true;
}
bool e2_agent::handle_e2_setup_response(e2setup_resp_s setup_response)
{
if (e2ap_.process_setup_response(setup_response)) {
logger.error("Failed to process E2 Setup Response \n");
ric_connected = false;
e2_setup_proc_t::e2setupresult res;
res.success = false;
e2_setup_proc.trigger(res);
return false;
}
ric_connected = true;
e2_setup_proc_t::e2setupresult res;
res.success = true;
e2_setup_proc.trigger(res);
return true;
}
bool e2_agent::handle_e2_unsuccessful_outcome(asn1::e2ap::unsuccessful_outcome_s& unsuccessful_outcome)
{
using namespace asn1::e2ap;
if (unsuccessful_outcome.value.type() == e2_ap_elem_procs_o::unsuccessful_outcome_c::types_opts::e2setup_fail) {
logger.info("Received E2AP E2 Setup Failure");
if (e2ap_.process_e2_setup_failure(unsuccessful_outcome.value.e2setup_fail())) {
logger.error("Failed to process E2 Setup Failure \n");
return false;
}
} else if (unsuccessful_outcome.value.type() ==
e2_ap_elem_procs_o::unsuccessful_outcome_c::types_opts::e2node_cfg_upd_fail) {
logger.info("Received E2node configuration update Failure");
if (e2ap_.process_e2_node_config_update_failure(unsuccessful_outcome.value.e2node_cfg_upd_fail())) {
logger.error("Failed to process E2node configuration update Failure \n");
return false;
}
} else if (unsuccessful_outcome.value.type() ==
e2_ap_elem_procs_o::unsuccessful_outcome_c::types_opts::ricservice_upd_fail) {
logger.info("Received E2AP RIC Service Update Failure \n");
if (e2ap_.process_ric_service_update_failure(unsuccessful_outcome.value.ricservice_upd_fail())) {
logger.error("Failed to process RIC service update failure \n");
return false;
}
} else if (unsuccessful_outcome.value.type() ==
e2_ap_elem_procs_o::unsuccessful_outcome_c::types_opts::e2_removal_fail) {
logger.info("Received E2AP removal Unsuccessful Outcome \n");
if (e2ap_.process_e2_removal_failure(unsuccessful_outcome.value.e2_removal_fail())) {
logger.error("Failed to process RIC service status failure \n");
return false;
}
} else {
logger.info("Received E2AP Unknown Unsuccessful Outcome \n");
}
return true;
}
bool e2_agent::handle_ric_subscription_request(ricsubscription_request_s ric_subscription_request)
{
logger.info("Received RIC Subscription Request from RIC ID: %i (instance id %i) to RAN Function ID: %i",
ric_subscription_request->ri_crequest_id->ric_requestor_id,
ric_subscription_request->ri_crequest_id->ric_instance_id,
ric_subscription_request->ra_nfunction_id->value);
if (e2ap_.process_subscription_request(ric_subscription_request)) {
logger.error("Failed to process RIC subscription request \n");
return false;
}
return true;
}
bool e2_agent::handle_ric_subscription_delete_request(ricsubscription_delete_request_s ricsubscription_delete_request)
{
logger.info("Received RIC Subscription Delete request from RIC ID: %i (instance id %i) to RAN Function ID: %i",
ricsubscription_delete_request->ri_crequest_id->ric_requestor_id,
ricsubscription_delete_request->ri_crequest_id->ric_instance_id,
ricsubscription_delete_request->ra_nfunction_id->value);
if (e2ap_.process_subscription_delete_request(ricsubscription_delete_request)) {
logger.error("Failed to process RIC subscription delete request \n");
return false;
}
return true;
}
bool e2_agent::handle_subscription_modification_request(uint32_t ric_subscription_modification_request)
{
if (e2ap_.process_subscription_modification_request(ric_subscription_modification_request)) {
logger.error("Failed to process RIC subscription delete request \n");
return false;
}
return true;
}
bool e2_agent::handle_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm)
{
if (e2ap_.process_subscription_modification_confirm(ric_subscription_modification_confirm)) {
logger.error("Failed to process RIC subscription delete request \n");
return false;
}
return true;
}
bool e2_agent::handle_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse)
{
if (e2ap_.process_subscription_modification_refuse(ric_subscription_modification_refuse)) {
logger.error("Failed to process RIC subscription delete request \n");
return false;
}
return true;
}
bool e2_agent::handle_reset_request(reset_request_s& reset_request)
{
printf("Received E2AP E2 Reset Request \n");
// call process to handle reset request, if it fails log error and return false, else return true - success
if (e2ap_.process_reset_request(reset_request)) {
logger.error("Failed to process E2 Reset Request \n");
return false;
}
logger.info("Reset transaction with ID = {}", e2ap_.get_reset_id());
// send reset reset response
auto send_reset_resp = [this]() { send_e2_msg(E2_RESET_RESPONSE); };
ric_rece_task_queue.push(send_reset_resp);
return true;
}
bool e2_agent::handle_reset_response(reset_resp_s& reset_response)
{
printf("Received E2AP E2 Reset Response \n");
// call process to handle reset reponse, if it fails log error, else return true - success
// all processing of message will be done in process_reset_response (e2ap.cc)
if (e2ap_.process_reset_response(reset_response)) {
logger.error("Failed to process E2 Reset Response \n");
return false;
}
logger.info("Reset Response successfully processed \n");
return true;
}

@ -0,0 +1,456 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsgnb/hdr/stack/ric/e2ap.h"
#include "srsgnb/hdr/stack/ric/e2_agent.h"
#include "srsgnb/hdr/stack/ric/e2ap_ric_subscription.h"
e2ap::e2ap(srslog::basic_logger& logger,
e2_agent* _e2_agent,
srsenb::e2_interface_metrics* _gnb_metrics,
srsran::task_scheduler* _task_sched_ptr) :
logger(logger), _e2_agent(_e2_agent), e2sm_(logger, _task_sched_ptr), task_sched_ptr(_task_sched_ptr)
{
gnb_metrics = _gnb_metrics;
if (task_sched_ptr) {
e2_procedure_timeout = task_sched_ptr->get_unique_timer();
}
// register SM to receive enb metrics
gnb_metrics->register_e2sm(&e2sm_);
// add SMs to map
uint32_t local_ran_function_id = 147;
RANfunction_description add_func;
add_func.function_instance = 0;
add_func.sm_ptr = &e2sm_;
ran_functions[local_ran_function_id] = add_func;
}
e2ap::~e2ap(){};
bool e2ap::get_func_desc(uint32_t ran_func_id, RANfunction_description& fdesc)
{
if (ran_functions.count(ran_func_id)) {
fdesc = ran_functions.at(ran_func_id);
return true;
}
return false;
}
bool e2ap::queue_send_e2ap_pdu(e2_ap_pdu_c e2ap_pdu)
{
if (_e2_agent) {
_e2_agent->queue_send_e2ap_pdu(e2ap_pdu);
}
return true;
}
e2_ap_pdu_c e2ap::generate_setup_request()
{
e2_ap_pdu_c pdu;
init_msg_s& initmsg = pdu.set_init_msg();
initmsg.load_info_obj(ASN1_E2AP_ID_E2SETUP);
e2setup_request_s& setup = initmsg.value.e2setup_request();
setup->transaction_id.crit = asn1::crit_opts::reject;
setup->transaction_id.value.value = setup_procedure_transaction_id;
setup->global_e2node_id.crit = asn1::crit_opts::reject;
auto& gnb_ = setup->global_e2node_id.value.set_gnb();
gnb_.global_g_nb_id.plmn_id.from_number(plmn_id);
gnb_.global_g_nb_id.gnb_id.gnb_id().from_number(gnb_id, 28); // eutra_cell_id has 28 bits
// add all supported e2SM functions
setup->ra_nfunctions_added.crit = asn1::crit_opts::reject;
auto& ra_nfunc_list = setup->ra_nfunctions_added.value;
ra_nfunc_list.resize(ran_functions.size());
uint32_t idx = 0;
for (auto& x : ran_functions) {
uint32_t local_ran_function_id = x.first;
e2sm* sm_ptr = x.second.sm_ptr;
ra_nfunction_item_s& ran_func = ra_nfunc_list[idx].value().ra_nfunction_item();
ran_func.ran_function_id = local_ran_function_id;
ran_func.ran_function_revision = sm_ptr->get_revision();
ran_func.ran_function_oid.from_string(sm_ptr->get_oid().c_str());
sm_ptr->generate_ran_function_description(x.second, ran_func);
idx++;
}
setup->e2node_component_cfg_addition.crit = asn1::crit_opts::reject;
auto& list1 = setup->e2node_component_cfg_addition.value;
list1.resize(1);
e2node_component_cfg_addition_item_s& item1 = list1[0].value().e2node_component_cfg_addition_item();
item1.e2node_component_interface_type = e2node_component_interface_type_opts::ng;
item1.e2node_component_id.set_e2node_component_interface_type_ng().amf_name.from_string("nginterf");
item1.e2node_component_cfg.e2node_component_request_part.from_string("72657170617274");
item1.e2node_component_cfg.e2node_component_resp_part.from_string("72657370617274");
return pdu;
}
e2_ap_pdu_c e2ap::generate_subscription_response(ric_subscription_reponse_t ric_subscription_reponse)
{
e2_ap_pdu_c pdu;
successful_outcome_s& success = pdu.set_successful_outcome();
success.load_info_obj(ASN1_E2AP_ID_RICSUBSCRIPTION);
success.crit = asn1::crit_opts::reject;
ricsubscription_resp_s& sub_resp = success.value.ricsubscription_resp();
sub_resp->ri_crequest_id.crit = asn1::crit_opts::reject;
sub_resp->ri_crequest_id.id = ASN1_E2AP_ID_RI_CREQUEST_ID;
sub_resp->ri_crequest_id.value.ric_requestor_id = ric_subscription_reponse.ric_requestor_id;
sub_resp->ri_crequest_id.value.ric_instance_id = ric_subscription_reponse.ric_instance_id;
sub_resp->ra_nfunction_id.crit = asn1::crit_opts::reject;
sub_resp->ra_nfunction_id.id = ASN1_E2AP_ID_RA_NFUNCTION_ID;
sub_resp->ra_nfunction_id->value = ric_subscription_reponse.ra_nfunction_id;
sub_resp->ri_cactions_admitted.crit = asn1::crit_opts::reject;
auto& action_admit_list = sub_resp->ri_cactions_admitted.value;
action_admit_list.resize(ric_subscription_reponse.admitted_actions.size());
for (uint32_t i = 0; i < ric_subscription_reponse.admitted_actions.size(); i++) {
action_admit_list[i].load_info_obj(ASN1_E2AP_ID_RI_CACTION_ADMITTED_ITEM);
ri_caction_admitted_item_s& a_item = action_admit_list[i]->ri_caction_admitted_item();
a_item.ric_action_id = ric_subscription_reponse.admitted_actions[i];
}
if (ric_subscription_reponse.not_admitted_actions.size()) {
sub_resp->ri_cactions_not_admitted.crit = asn1::crit_opts::reject;
auto& action_not_admit_list = sub_resp->ri_cactions_not_admitted.value;
action_not_admit_list.resize(ric_subscription_reponse.not_admitted_actions.size());
for (uint32_t i = 0; i < ric_subscription_reponse.not_admitted_actions.size(); i++) {
action_not_admit_list[i].load_info_obj(ASN1_E2AP_ID_RI_CACTION_NOT_ADMITTED_ITEM);
ri_caction_not_admitted_item_s& not_a_item = action_not_admit_list[i]->ri_caction_not_admitted_item();
not_a_item.ric_action_id = ric_subscription_reponse.not_admitted_actions[i];
not_a_item.cause.set_misc(); // TODO: support cause properly
}
}
return pdu;
}
e2_ap_pdu_c e2ap::generate_subscription_failure(ric_subscription_reponse_t ric_subscription_reponse)
{
e2_ap_pdu_c pdu;
unsuccessful_outcome_s& failure = pdu.set_unsuccessful_outcome();
failure.load_info_obj(ASN1_E2AP_ID_RICSUBSCRIPTION);
failure.crit = asn1::crit_opts::reject;
ricsubscription_fail_s& sub_resp = failure.value.ricsubscription_fail();
sub_resp->ri_crequest_id.crit = asn1::crit_opts::reject;
sub_resp->ri_crequest_id.id = ASN1_E2AP_ID_RI_CREQUEST_ID;
sub_resp->ri_crequest_id.value.ric_requestor_id = ric_subscription_reponse.ric_requestor_id;
sub_resp->ri_crequest_id.value.ric_instance_id = ric_subscription_reponse.ric_instance_id;
sub_resp->ra_nfunction_id.crit = asn1::crit_opts::reject;
sub_resp->ra_nfunction_id.id = ASN1_E2AP_ID_RA_NFUNCTION_ID;
sub_resp->ra_nfunction_id->value = ric_subscription_reponse.ra_nfunction_id;
sub_resp->cause->set_misc(); // TODO: set the cause and crit_diagnostics properly
sub_resp->crit_diagnostics_present = false;
return pdu;
}
e2_ap_pdu_c e2ap::generate_subscription_delete_response(ric_subscription_reponse_t ric_subscription_reponse)
{
e2_ap_pdu_c pdu;
successful_outcome_s& success = pdu.set_successful_outcome();
success.load_info_obj(ASN1_E2AP_ID_RICSUBSCRIPTION_DELETE);
success.crit = asn1::crit_opts::reject;
ricsubscription_delete_resp_s& sub_resp = success.value.ricsubscription_delete_resp();
sub_resp->ri_crequest_id.crit = asn1::crit_opts::reject;
sub_resp->ri_crequest_id->ric_requestor_id = ric_subscription_reponse.ric_requestor_id;
sub_resp->ri_crequest_id->ric_instance_id = ric_subscription_reponse.ric_instance_id;
sub_resp->ra_nfunction_id.crit = asn1::crit_opts::reject;
sub_resp->ra_nfunction_id->value = ric_subscription_reponse.ra_nfunction_id;
return pdu;
}
e2_ap_pdu_c e2ap::generate_subscription_delete_failure(ric_subscription_reponse_t ric_subscription_reponse)
{
e2_ap_pdu_c pdu;
unsuccessful_outcome_s& failure = pdu.set_unsuccessful_outcome();
failure.load_info_obj(ASN1_E2AP_ID_RICSUBSCRIPTION);
failure.crit = asn1::crit_opts::reject;
ricsubscription_delete_fail_s& sub_resp = failure.value.ricsubscription_delete_fail();
sub_resp->ri_crequest_id.crit = asn1::crit_opts::reject;
sub_resp->ri_crequest_id.id = ASN1_E2AP_ID_RI_CREQUEST_ID;
sub_resp->ri_crequest_id.value.ric_requestor_id = ric_subscription_reponse.ric_requestor_id;
sub_resp->ri_crequest_id.value.ric_instance_id = ric_subscription_reponse.ric_instance_id;
sub_resp->ra_nfunction_id.crit = asn1::crit_opts::reject;
sub_resp->ra_nfunction_id.id = ASN1_E2AP_ID_RA_NFUNCTION_ID;
sub_resp->ra_nfunction_id->value = ric_subscription_reponse.ra_nfunction_id;
sub_resp->cause->set_misc(); // TODO: set the cause and crit_diagnostics properly
sub_resp->crit_diagnostics_present = false;
return pdu;
}
e2_ap_pdu_c e2ap::generate_subscription_delete_required(ric_subscription_reponse_t ric_subscription_reponse)
{
// TODO: available in e2ap-v3
e2_ap_pdu_c pdu;
return pdu;
}
e2_ap_pdu_c e2ap::generate_subscription_modification_response()
{
// TODO: available in e2ap-v3
e2_ap_pdu_c pdu;
return pdu;
}
e2_ap_pdu_c e2ap::generate_subscription_modification_failure()
{
// TODO: available in e2ap-v3
e2_ap_pdu_c pdu;
return pdu;
}
e2_ap_pdu_c e2ap::generate_subscription_modification_required()
{
// TODO: available in e2ap-v3
e2_ap_pdu_c pdu;
return pdu;
}
int e2ap::process_setup_response(e2setup_resp_s setup_response)
{
if (setup_response->transaction_id.value.value == 0) {
// TODO: transaction_id reset? check specs
setup_procedure_transaction_id = 0;
}
if (setup_procedure_transaction_id == setup_response->transaction_id.value.value) {
setup_procedure_transaction_id++;
e2_established = true;
} else {
logger.error("Received setup response with wrong transaction id");
return SRSRAN_ERROR;
}
global_ric_id.plmn_id = setup_response->global_ric_id.value.plmn_id.to_number();
global_ric_id.ric_id = setup_response->global_ric_id.value.ric_id.to_number();
if (setup_response->ra_nfunctions_accepted_present) {
for (int i = 0; i < (int)setup_response->ra_nfunctions_accepted.value.size(); i++) {
uint32_t ran_func_id = setup_response->ra_nfunctions_accepted.value[i]->ra_nfunction_id_item().ran_function_id;
if (ran_functions.find(ran_func_id) == ran_functions.end()) {
logger.error("Received setup response with unknown ran function id %d", ran_func_id);
} else {
logger.info("Received setup response with ran function id %d", ran_func_id);
ran_functions[ran_func_id].accepted = true;
}
}
}
return SRSRAN_SUCCESS;
}
int e2ap::process_subscription_request(ricsubscription_request_s ric_subscription_request)
{
std::unique_ptr<e2ap::ric_subscription> new_ric_subs =
std::make_unique<e2ap::ric_subscription>(this, ric_subscription_request);
if (new_ric_subs->is_initialized()) {
new_ric_subs->start_subscription();
active_subscriptions.push_back(std::move(new_ric_subs));
} else {
return false;
}
return SRSRAN_SUCCESS;
}
int e2ap::process_subscription_delete_request(ricsubscription_delete_request_s ricsubscription_delete_request)
{
bool ric_subs_found = false;
for (auto it = active_subscriptions.begin(); it != active_subscriptions.end(); it++) {
if ((**it).get_ric_requestor_id() == ricsubscription_delete_request->ri_crequest_id->ric_requestor_id and
(**it).get_ric_instance_id() == ricsubscription_delete_request->ri_crequest_id->ric_instance_id) {
ric_subs_found = true;
(**it).delete_subscription();
active_subscriptions.erase(it);
break;
}
}
if (not ric_subs_found) {
// TODO: send failure
}
return SRSRAN_SUCCESS;
}
int e2ap::process_subscription_modification_request(uint32_t ric_subscription_modification_request)
{
// TODO: implement, here only placeholder
return SRSRAN_SUCCESS;
}
int e2ap::process_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm)
{
// TODO: implement, here only placeholder
return SRSRAN_SUCCESS;
}
int e2ap::process_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse)
{
// TODO: implement, here only placeholder
return SRSRAN_SUCCESS;
}
e2_ap_pdu_c e2ap::generate_indication(ric_indication_t& ric_indication)
{
using namespace asn1::e2ap;
e2_ap_pdu_c pdu;
init_msg_s& initmsg = pdu.set_init_msg();
initmsg.load_info_obj(ASN1_E2AP_ID_RI_CIND);
initmsg.crit = asn1::crit_opts::reject;
ri_cind_s& indication = initmsg.value.ri_cind();
indication->ri_crequest_id.crit = asn1::crit_opts::reject;
indication->ri_crequest_id.value.ric_requestor_id = ric_indication.ric_requestor_id;
indication->ri_crequest_id.value.ric_instance_id = ric_indication.ric_instance_id;
indication->ra_nfunction_id.crit = asn1::crit_opts::reject;
indication->ra_nfunction_id.value = ric_indication.ra_nfunction_id;
indication->ri_caction_id.crit = asn1::crit_opts::reject;
indication->ri_caction_id.value = ric_indication.ri_caction_id;
if (ric_indication.ri_indication_sn_present) {
indication->ri_cind_sn_present = true;
indication->ri_cind_sn.crit = asn1::crit_opts::reject;
indication->ri_cind_sn->value = ric_indication.ri_indication_sn;
}
indication->ri_cind_type.crit = asn1::crit_opts::reject;
indication->ri_cind_type.value = ric_indication.indication_type;
indication->ri_cind_hdr.crit = asn1::crit_opts::reject;
indication->ri_cind_hdr->resize(ric_indication.ri_cind_hdr->N_bytes);
std::copy(ric_indication.ri_cind_hdr->msg,
ric_indication.ri_cind_hdr->msg + ric_indication.ri_cind_hdr->N_bytes,
indication->ri_cind_hdr->data());
indication->ri_cind_msg.crit = asn1::crit_opts::reject;
indication->ri_cind_msg->resize(ric_indication.ri_cind_msg->N_bytes);
std::copy(ric_indication.ri_cind_msg->msg,
ric_indication.ri_cind_msg->msg + ric_indication.ri_cind_msg->N_bytes,
indication->ri_cind_msg->data());
return pdu;
}
e2_ap_pdu_c e2ap::generate_reset_request()
{
using namespace asn1::e2ap;
e2_ap_pdu_c pdu;
init_msg_s& request = pdu.set_init_msg();
request.load_info_obj(ASN1_E2AP_ID_RESET);
reset_request_s& reset_request = request.value.reset_request();
reset_request->transaction_id.crit = asn1::crit_opts::reject;
reset_request->transaction_id.value.value = reset_transaction_id;
reset_request->cause.crit = asn1::crit_opts::ignore;
reset_request->cause.value.set_misc();
return pdu;
}
e2_ap_pdu_c e2ap::generate_reset_response()
{
e2_ap_pdu_c pdu;
successful_outcome_s& response = pdu.set_successful_outcome();
response.load_info_obj(ASN1_E2AP_ID_RESET);
reset_resp_s& reset_response = response.value.reset_resp();
reset_response->transaction_id.crit = asn1::crit_opts::reject;
reset_response->transaction_id.value.value = reset_transaction_id;
return pdu;
}
int e2ap::process_reset_request(reset_request_s reset_request)
{
reset_id = reset_request->transaction_id.value;
// TODO: Parse and store the cause for future extension of the e2_agent
return SRSRAN_SUCCESS;
}
int e2ap::process_reset_response(reset_resp_s reset_response)
{
// TO DO process reset response from RIC
reset_response_received = true;
return SRSRAN_SUCCESS;
}
int e2ap::get_reset_id()
{
return reset_id;
}
// implementation of e2ap failure functions
int e2ap::process_e2_setup_failure(e2setup_fail_s e2setup_failure)
{
if (e2setup_failure->transaction_id.value.value == 0) {
// TODO: transaction_id reset? check specs
setup_procedure_transaction_id = 0;
}
if (setup_procedure_transaction_id == e2setup_failure->transaction_id.value.value) {
setup_procedure_transaction_id++;
} else {
logger.error("Received setup failure with wrong transaction id");
}
if (e2setup_failure->tn_linfo_present) {
logger.error("Received setup failure with transport layer info");
}
if (e2setup_failure->time_to_wait_present) {
logger.error("Received setup failure with time to wait");
e2_procedure_timeout.set(e2setup_failure->time_to_wait.value.to_number(), [this](int trans_id) {
logger.info("E2AP procedure timeout expired transaction id %d", trans_id);
pending_e2_setup = false;
});
e2_procedure_timeout.run();
}
return SRSRAN_SUCCESS;
}
int e2ap::process_e2_node_config_update_failure(e2node_cfg_upd_fail_s e2node_config_update_failure)
{
pending_e2_node_config_update = false;
return SRSRAN_SUCCESS;
}
int e2ap::process_ric_service_update_failure(ricservice_upd_fail_s service_update_failure)
{
pending_ric_service_update = false;
return SRSRAN_SUCCESS;
}
int e2ap::process_e2_removal_failure(e2_removal_fail_s e2removal_failure)
{
pending_e2_removal = false;
return SRSRAN_SUCCESS;
}

@ -0,0 +1,218 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#include "srsgnb/hdr/stack/ric/e2ap_ric_subscription.h"
e2ap::ric_subscription::ric_subscription(e2ap* e2ap, ricsubscription_request_s ric_subscription_request) :
parent(e2ap),
initialized(false),
ric_requestor_id(ric_subscription_request->ri_crequest_id->ric_requestor_id),
ric_instance_id(ric_subscription_request->ri_crequest_id->ric_instance_id),
ra_nfunction_id(ric_subscription_request->ra_nfunction_id->value),
reporting_timer(parent->task_sched_ptr->get_unique_timer())
{
RANfunction_description ran_func_desc;
if (!parent->get_func_desc(ra_nfunction_id, ran_func_desc)) {
parent->logger.debug("Cannot find RAN function with ID: %i\n", ra_nfunction_id);
this->_send_subscription_failure();
return;
}
sm_ptr = ran_func_desc.sm_ptr;
if (sm_ptr == nullptr) {
parent->logger.debug("No valid pointer to SM with RAN function id: %i\n", ra_nfunction_id);
this->_send_subscription_failure();
return;
}
RIC_event_trigger_definition_t event_trigger;
if (sm_ptr->process_ric_event_trigger_definition(ric_subscription_request, event_trigger)) {
if (event_trigger.type == RIC_event_trigger_definition_t::e2sm_event_trigger_type_t::E2SM_REPORT) {
reporting_period = event_trigger.report_period;
reporting_period = 3000; // TODO: to remove, keep it 3s for testing
}
}
ri_cactions_to_be_setup_list_l& action_list =
ric_subscription_request->ricsubscription_details->ric_action_to_be_setup_list;
for (uint32_t i = 0; i < action_list.size(); i++) {
ri_caction_to_be_setup_item_s action_item = action_list[i]->ri_caction_to_be_setup_item();
E2AP_RIC_action_t candidate_action;
candidate_action.ric_action_id = action_item.ric_action_id;
candidate_action.ric_action_type = action_item.ric_action_type;
if (sm_ptr->process_ric_action_definition(action_item, candidate_action)) {
parent->logger.debug("Admitted action %i (type: %i), mapped to SM local action ID: %i",
candidate_action.ric_action_id,
candidate_action.ric_action_type,
candidate_action.sm_local_ric_action_id);
printf("Admitted action %i, mapped to SM local action ID: %i\n",
candidate_action.ric_action_id,
candidate_action.sm_local_ric_action_id);
admitted_actions.push_back(candidate_action);
if (action_item.ric_subsequent_action_present) {
parent->logger.debug("--Action %i (type: %i) contains subsequent action of type %i with wait time: %i",
action_item.ric_action_id,
action_item.ric_action_type,
action_item.ric_subsequent_action.ric_subsequent_action_type,
action_item.ric_subsequent_action.ric_time_to_wait);
}
} else {
parent->logger.debug("Not admitted action %i (type: %i)", action_item.ric_action_id, action_item.ric_action_type);
not_admitted_actions.push_back(action_item.ric_action_id);
}
}
if (admitted_actions.size() == 0) {
parent->logger.debug("No Action admitted -> remove subscription for RAN function id: %i", ra_nfunction_id);
printf("No Action admitted -> remove subscription for RAN function id: %i\n", ra_nfunction_id);
this->_send_subscription_failure();
return;
}
initialized = true;
}
void e2ap::ric_subscription::start_subscription()
{
this->_send_subscription_response();
if (reporting_period) {
printf("Start sending RIC indication msgs every %i ms\n", reporting_period);
parent->logger.debug("Start sending RIC indication msgs every %i ms", reporting_period);
reporting_timer.set(reporting_period, [this](uint32_t tid) { _send_ric_indication(); });
reporting_timer.run();
}
}
void e2ap::ric_subscription::_send_subscription_response()
{
parent->logger.debug("Send RIC Subscription Response to RIC Requestor ID: %i\n", ric_requestor_id);
ric_subscription_reponse_t ric_subscription_reponse;
ric_subscription_reponse.ric_requestor_id = ric_requestor_id;
ric_subscription_reponse.ric_instance_id = ric_instance_id;
ric_subscription_reponse.ra_nfunction_id = ra_nfunction_id;
for (auto& action : admitted_actions) {
ric_subscription_reponse.admitted_actions.push_back(action.ric_action_id);
}
for (auto& action : not_admitted_actions) {
ric_subscription_reponse.not_admitted_actions.push_back(action);
}
e2_ap_pdu_c send_pdu = parent->generate_subscription_response(ric_subscription_reponse);
parent->queue_send_e2ap_pdu(send_pdu);
}
void e2ap::ric_subscription::_send_subscription_failure()
{
parent->logger.debug("Send RIC Subscription Failure Response to RIC Requestor ID: %i\n", ric_requestor_id);
ric_subscription_reponse_t ric_subscription_reponse;
ric_subscription_reponse.ric_requestor_id = ric_requestor_id;
ric_subscription_reponse.ric_instance_id = ric_instance_id;
ric_subscription_reponse.ra_nfunction_id = ra_nfunction_id;
e2_ap_pdu_c send_pdu = parent->generate_subscription_failure(ric_subscription_reponse);
parent->queue_send_e2ap_pdu(send_pdu);
}
void e2ap::ric_subscription::delete_subscription()
{
if (reporting_timer.is_running()) {
parent->logger.debug("Stop sending RIC indication msgs");
reporting_timer.stop();
}
ric_subscription_reponse_t ric_subscription_reponse;
ric_subscription_reponse.ric_requestor_id = ric_requestor_id;
ric_subscription_reponse.ric_instance_id = ric_instance_id;
ric_subscription_reponse.ra_nfunction_id = ra_nfunction_id;
// remove registered actions from SM
if (sm_ptr) {
for (auto& action : admitted_actions) {
sm_ptr->remove_ric_action_definition(action);
}
} else {
e2_ap_pdu_c send_pdu = parent->generate_subscription_delete_failure(ric_subscription_reponse);
parent->queue_send_e2ap_pdu(send_pdu);
return;
}
parent->logger.debug("Send RIC Subscription Delete Response to RIC Requestor ID: %i\n", ric_requestor_id);
e2_ap_pdu_c send_pdu = parent->generate_subscription_delete_response(ric_subscription_reponse);
parent->queue_send_e2ap_pdu(send_pdu);
}
bool e2ap::ric_subscription::process_subscription_modification_request(uint32_t ric_subscription_modification_request)
{
// TODO: implement, currently not supported in ans1
return false;
}
bool e2ap::ric_subscription::process_subscription_modification_confirm(uint32_t ric_subscription_modification_confirm)
{
// TODO: implement, currently not supported in ans1
return false;
}
bool e2ap::ric_subscription::process_subscription_modification_refuse(uint32_t ric_subscription_modification_refuse)
{
// TODO: implement, currently not supported in ans1
return false;
}
uint32_t e2ap::ric_subscription::_generate_ric_indication_sn()
{
uint32_t sn = _ric_indication_sn_gen;
_ric_indication_sn_gen++;
if (_ric_indication_sn_gen > 65535) {
_ric_indication_sn_gen = 0;
}
return sn;
};
void e2ap::ric_subscription::_send_ric_indication()
{
if (sm_ptr == nullptr) {
parent->logger.error("SM pointer not set in subscription: %i\n", ric_requestor_id);
return;
}
for (auto& action : admitted_actions) {
printf("Sending RIC indication msg to RIC Requestor ID: %i\n", ric_requestor_id);
ric_indication_t ric_indication;
ric_indication.ric_requestor_id = ric_requestor_id;
ric_indication.ric_instance_id = ric_instance_id;
ric_indication.ra_nfunction_id = ra_nfunction_id;
ric_indication.ri_caction_id = action.ric_action_id;
ric_indication.ri_indication_sn_present = true;
ric_indication.ri_indication_sn = _generate_ric_indication_sn();
if (sm_ptr->generate_ric_indication_content(action, ric_indication)) {
e2_ap_pdu_c send_pdu = parent->generate_indication(ric_indication);
parent->queue_send_e2ap_pdu(send_pdu);
}
}
// reschedule sending RIC indication
if (reporting_period) {
reporting_timer.run();
}
}

@ -0,0 +1,452 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#include "srsgnb/hdr/stack/ric/e2sm_kpm.h"
#include "srsgnb/hdr/stack/ric/e2sm_kpm_metrics.h"
#include "srsgnb/hdr/stack/ric/e2sm_kpm_report_service.h"
#include <numeric>
const std::string e2sm_kpm::short_name = "ORAN-E2SM-KPM";
const std::string e2sm_kpm::oid = "1.3.6.1.4.1.53148.1.2.2.2";
const std::string e2sm_kpm::func_description = "KPM Monitor";
const uint32_t e2sm_kpm::revision = 0;
e2sm_kpm::e2sm_kpm(srslog::basic_logger& logger_, srsran::task_scheduler* _task_sched_ptr) :
e2sm(short_name, oid, func_description, revision, _task_sched_ptr), logger(logger_)
{
random_gen = srsran_random_init(1234);
// add supported metrics
for (auto& metric : get_e2sm_kpm_28_552_metrics()) {
if (metric.supported) {
supported_meas_types.push_back(metric);
}
}
for (auto& metric : get_e2sm_kpm_34_425_metrics()) {
if (metric.supported) {
supported_meas_types.push_back(metric);
}
}
for (auto& metric : e2sm_kpm_oran_metrics()) {
if (metric.supported) {
supported_meas_types.push_back(metric);
}
}
for (auto& metric : e2sm_kpm_custom_metrics()) {
if (metric.supported) {
supported_meas_types.push_back(metric);
}
}
}
e2sm_kpm::~e2sm_kpm()
{
srsran_random_free(random_gen);
}
bool e2sm_kpm::generate_ran_function_description(RANfunction_description& desc, ra_nfunction_item_s& ran_func)
{
desc.function_shortname = short_name;
desc.function_e2_sm_oid = oid;
desc.function_desc = func_description;
e2_sm_kpm_ra_nfunction_description_s e2sm_kpm_ra_nfunction_description;
e2sm_kpm_ra_nfunction_description.ran_function_name.ran_function_short_name.from_string(short_name.c_str());
e2sm_kpm_ra_nfunction_description.ran_function_name.ran_function_e2_sm_oid.from_string(oid.c_str());
e2sm_kpm_ra_nfunction_description.ran_function_name.ran_function_description.from_string(func_description.c_str());
if (desc.function_instance) {
e2sm_kpm_ra_nfunction_description.ran_function_name.ran_function_instance_present = true;
e2sm_kpm_ra_nfunction_description.ran_function_name.ran_function_instance = desc.function_instance;
}
// O-RAN.WG3.E2SM-KPM-R003-v03.00, 7.3.1 Event Trigger Style Types
auto& event_trigger_style_list = e2sm_kpm_ra_nfunction_description.ric_event_trigger_style_list;
event_trigger_style_list.resize(1);
event_trigger_style_list[0].ric_event_trigger_style_type = 1;
event_trigger_style_list[0].ric_event_trigger_style_name.from_string("Periodic report");
event_trigger_style_list[0].ric_event_trigger_format_type = 1; // uses RIC Event Trigger Definition Format 1
// O-RAN.WG3.E2SM-KPM-R003-v03.00, 7.4.1 REPORT Service Style Type
auto& report_style_list = e2sm_kpm_ra_nfunction_description.ric_report_style_list;
report_style_list.resize(1);
report_style_list[0].ric_report_style_type = 1;
report_style_list[0].ric_report_style_name.from_string("E2 Node Measurement");
report_style_list[0].ric_action_format_type = 1;
report_style_list[0].ric_ind_hdr_format_type = 1;
report_style_list[0].ric_ind_msg_format_type = 1;
std::vector<std::string> supported_enb_meas = _get_supported_meas(ENB_LEVEL | CELL_LEVEL);
for (const auto& metric : supported_enb_meas) {
meas_info_action_item_s meas_info_item;
meas_info_item.meas_name.from_string(metric.c_str());
report_style_list[0].meas_info_action_list.push_back(meas_info_item);
break; // TODO: add only one as flexric does not like long setup_request msg and crashes
}
/* TODO: seems that flexric does not like long setup_request msg and crashes, note: wireshark decodes it correctly
// see: nearRT-RIC: flexric/src/ric/msg_handler_ric.c:88:
// generate_setup_response: Assertion `req->ran_func_item[i].def.len < 127' failed
report_style_list[1].ric_report_style_type = 2;
report_style_list[1].ric_report_style_name.from_string("E2 Node Measurement for a single UE");
report_style_list[1].ric_action_format_type = 2;
report_style_list[1].ric_ind_hdr_format_type = 1;
report_style_list[1].ric_ind_msg_format_type = 1;
// TODO: add all supported UE LEVEL metrics
report_style_list[1].meas_info_action_list.resize(1);
report_style_list[1].meas_info_action_list[0].meas_name.from_string("RRU.PrbTotDl");
// A measurement ID can be used for subscription instead of a measurement type if an identifier of a certain
// measurement type was exposed by an E2 Node via the RAN Function Definition IE.
// measurement name to ID mapping (local to the E2 node), here only an example:
// report_style_list[1].meas_info_action_list[0].meas_id = 123;
report_style_list[2].ric_report_style_type = 3;
report_style_list[2].ric_report_style_name.from_string("Condition-based, UE-level E2 Node Measurement");
report_style_list[2].ric_action_format_type = 3;
report_style_list[2].ric_ind_hdr_format_type = 1;
report_style_list[2].ric_ind_msg_format_type = 2;
// TODO: add all supported UE LEVEL metrics
report_style_list[2].meas_info_action_list.resize(1);
report_style_list[2].meas_info_action_list[0].meas_name.from_string("RRU.PrbTotDl");
report_style_list[3].ric_report_style_type = 4;
report_style_list[3].ric_report_style_name.from_string("Common Condition-based, UE-level Measurement");
report_style_list[3].ric_action_format_type = 4;
report_style_list[3].ric_ind_hdr_format_type = 1;
report_style_list[3].ric_ind_msg_format_type = 3;
// TODO: add all supported UE LEVEL metrics
report_style_list[3].meas_info_action_list.resize(1);
report_style_list[3].meas_info_action_list[0].meas_name.from_string("RRU.PrbTotDl");
report_style_list[4].ric_report_style_type = 5;
report_style_list[4].ric_report_style_name.from_string("E2 Node Measurement for multiple UEs");
report_style_list[4].ric_action_format_type = 5;
report_style_list[4].ric_ind_hdr_format_type = 1;
report_style_list[4].ric_ind_msg_format_type = 3;
// TODO: add all supported UE LEVEL metrics
report_style_list[4].meas_info_action_list.resize(1);
report_style_list[4].meas_info_action_list[0].meas_name.from_string("RRU.PrbTotDl");
*/
logger.info("Generating RAN function description");
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
if (e2sm_kpm_ra_nfunction_description.pack(bref) != asn1::SRSASN_SUCCESS) {
printf("Failed to pack TX E2 PDU\n");
return false;
}
buf->N_bytes = bref.distance_bytes();
ran_func.ran_function_definition.resize(buf->N_bytes);
std::copy(buf->msg, buf->msg + buf->N_bytes, ran_func.ran_function_definition.data());
return true;
}
bool e2sm_kpm::process_ric_event_trigger_definition(ricsubscription_request_s subscription_request,
RIC_event_trigger_definition_t& event_def)
{
e2_sm_kpm_event_trigger_definition_s trigger_def;
asn1::cbit_ref bref(subscription_request->ricsubscription_details->ric_event_trigger_definition.data(),
subscription_request->ricsubscription_details->ric_event_trigger_definition.size());
if (trigger_def.unpack(bref) != asn1::SRSASN_SUCCESS) {
return false;
}
event_def.type = RIC_event_trigger_definition_t::e2sm_event_trigger_type_t::E2SM_REPORT;
event_def.report_period = trigger_def.event_definition_formats.event_definition_format1().report_period;
return true;
}
bool e2sm_kpm::process_ric_action_definition(ri_caction_to_be_setup_item_s ric_action, E2AP_RIC_action_t& action_entry)
{
bool admit_action = false;
e2_sm_kpm_action_definition_s e2sm_kpm_action_def;
asn1::cbit_ref bref(ric_action.ric_action_definition.data(), ric_action.ric_action_definition.size());
if (e2sm_kpm_action_def.unpack(bref) != asn1::SRSASN_SUCCESS) {
return false;
}
action_entry.sm_local_ric_action_id = _get_local_action_id();
e2sm_kpm_report_service* report_service;
switch (e2sm_kpm_action_def.ric_style_type) {
case 1:
admit_action = e2sm_kpm_report_service_style1::process_ric_action_definition(this, e2sm_kpm_action_def);
if (admit_action) {
report_service =
new e2sm_kpm_report_service_style1(this, action_entry.sm_local_ric_action_id, e2sm_kpm_action_def);
}
break;
case 2:
admit_action = e2sm_kpm_report_service_style2::process_ric_action_definition(this, e2sm_kpm_action_def);
if (admit_action) {
report_service =
new e2sm_kpm_report_service_style2(this, action_entry.sm_local_ric_action_id, e2sm_kpm_action_def);
}
break;
case 3:
admit_action = e2sm_kpm_report_service_style3::process_ric_action_definition(this, e2sm_kpm_action_def);
if (admit_action) {
report_service =
new e2sm_kpm_report_service_style3(this, action_entry.sm_local_ric_action_id, e2sm_kpm_action_def);
}
break;
case 4:
admit_action = e2sm_kpm_report_service_style4::process_ric_action_definition(this, e2sm_kpm_action_def);
if (admit_action) {
report_service =
new e2sm_kpm_report_service_style4(this, action_entry.sm_local_ric_action_id, e2sm_kpm_action_def);
}
break;
case 5:
admit_action = e2sm_kpm_report_service_style5::process_ric_action_definition(this, e2sm_kpm_action_def);
if (admit_action) {
report_service =
new e2sm_kpm_report_service_style5(this, action_entry.sm_local_ric_action_id, e2sm_kpm_action_def);
}
break;
default:
logger.info("Unknown RIC style type %i -> do not admit action %i (type %i)",
e2sm_kpm_action_def.ric_style_type,
ric_action.ric_action_id,
ric_action.ric_action_type);
return false;
}
if (not admit_action) {
return false;
}
_generate_new_local_action_id();
registered_actions_data.insert(
std::pair<uint32_t, e2sm_kpm_report_service*>(action_entry.sm_local_ric_action_id, report_service));
return admit_action;
}
bool e2sm_kpm::remove_ric_action_definition(E2AP_RIC_action_t& action_entry)
{
if (registered_actions_data.count(action_entry.sm_local_ric_action_id)) {
registered_actions_data.at(action_entry.sm_local_ric_action_id)->stop();
delete registered_actions_data.at(action_entry.sm_local_ric_action_id);
registered_actions_data.erase(action_entry.sm_local_ric_action_id);
return true;
}
return false;
}
bool e2sm_kpm::generate_ric_indication_content(E2AP_RIC_action_t& action_entry, ric_indication_t& ric_indication)
{
uint32_t action_id = action_entry.sm_local_ric_action_id;
if (!registered_actions_data.count(action_id)) {
logger.info("Unknown RIC action ID: %i (type %i) (SM local RIC action ID: %i)",
action_entry.ric_action_id,
action_entry.ric_action_type,
action_entry.sm_local_ric_action_id);
return false;
}
e2sm_kpm_report_service* report_service = registered_actions_data.at(action_id);
if (not report_service->is_ric_ind_ready()) {
return false;
}
ric_indication.indication_type = ri_cind_type_opts::report;
// header is the same for all RIC service styles, i.e., type 1
ric_indication.ri_cind_hdr = srsran::make_byte_buffer();
this->_generate_indication_header(report_service->get_ind_hdr(), ric_indication.ri_cind_hdr);
logger.info("Generating E2-SM-KPM Indication Message");
ric_indication.ri_cind_msg = srsran::make_byte_buffer();
this->_generate_indication_message(report_service->get_ind_msg(), ric_indication.ri_cind_msg);
// clear data collected for this action
report_service->clear_collected_data();
return true;
}
bool e2sm_kpm::_generate_indication_header(e2_sm_kpm_ind_hdr_s& hdr, srsran::unique_byte_buffer_t& buf)
{
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
if (hdr.pack(bref) != asn1::SRSASN_SUCCESS) {
printf("IND HEADER: Failed to pack TX E2 PDU\n");
return false;
}
buf->N_bytes = bref.distance_bytes();
return true;
}
bool e2sm_kpm::_generate_indication_message(e2_sm_kpm_ind_msg_s& msg, srsran::unique_byte_buffer_t& buf)
{
logger.info("Generating E2-SM-KPM Indication Message");
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
if (msg.pack(bref) != asn1::SRSASN_SUCCESS) {
printf("IND MSG: Failed to pack TX E2 PDU\n");
return false;
}
buf->N_bytes = bref.distance_bytes();
return true;
}
bool e2sm_kpm::_get_meas_definition(std::string meas_name, e2sm_kpm_metric_t& def)
{
auto name_matches = [&meas_name](const e2sm_kpm_metric_t& x) {
return (x.name == meas_name.c_str() or x.name == meas_name);
};
auto it = std::find_if(supported_meas_types.begin(), supported_meas_types.end(), name_matches);
if (it == supported_meas_types.end()) {
return false;
}
def = *it;
return true;
}
std::vector<std::string> e2sm_kpm::_get_supported_meas(uint32_t level_mask)
{
std::vector<std::string> supported_meas;
for (auto& metric : supported_meas_types) {
if ((level_mask & ENB_LEVEL) and (metric.supported_scopes & ENB_LEVEL)) {
supported_meas.push_back(metric.name);
} else if ((level_mask & CELL_LEVEL) and (metric.supported_scopes & CELL_LEVEL)) {
supported_meas.push_back(metric.name);
} else if ((level_mask & UE_LEVEL) and (metric.supported_scopes & UE_LEVEL)) {
supported_meas.push_back(metric.name);
} else if ((level_mask & BEARER_LEVEL) and (metric.supported_scopes & BEARER_LEVEL)) {
supported_meas.push_back(metric.name);
}
}
return supported_meas;
}
void e2sm_kpm::receive_e2_metrics_callback(const enb_metrics_t& m)
{
last_enb_metrics = m;
logger.debug("e2sm_kpm received new enb metrics, CPU0 Load: %.1f", last_enb_metrics.sys.cpu_load[0]);
}
bool e2sm_kpm::_collect_meas_value(e2sm_kpm_meas_def_t& meas_value, meas_record_item_c& item)
{
// here we implement logic of measurement data collection, currently we only read from enb_metrics
if (meas_value.data_type == meas_record_item_c::types::options::integer) {
uint32_t value;
if (_extract_integer_type_meas_value(meas_value, last_enb_metrics, value)) {
item.set_integer() = value;
return true;
}
} else {
// data_type == meas_record_item_c::types::options::real;
float value;
if (_extract_real_type_meas_value(meas_value, last_enb_metrics, value)) {
real_s real_value;
// TODO: real value seems to be not supported in asn1???
// real_value.value = value;
item.set_real() = real_value;
return true;
}
}
return false;
}
bool e2sm_kpm::_extract_integer_type_meas_value(e2sm_kpm_meas_def_t& meas_value,
const enb_metrics_t& enb_metrics,
uint32_t& value)
{
// TODO: maybe add ID to metric types in e2sm_kpm_metrics definitions, so we do not have to compare strings?
// TODO: make string comparison case insensitive
// all integer type measurements
// test: no_label
if (meas_value.name.c_str() == std::string("test")) {
switch (meas_value.label) {
case NO_LABEL:
if (meas_value.scope & ENB_LEVEL) {
value = (int32_t)enb_metrics.sys.cpu_load[0];
printf("extract last \"test\" value as int, (filled with ENB_LEVEL metric: CPU0_load) value %i \n", value);
return true;
}
if (meas_value.scope & CELL_LEVEL) {
uint32_t cell_id = meas_value.cell_id;
value = (int32_t)enb_metrics.stack.mac.cc_info[cell_id].cc_rach_counter;
printf("extract last \"test\" value as int, (filled with CELL_LEVEL metric: cc_rach_counter) value %i \n",
value);
return true;
}
if (meas_value.scope & UE_LEVEL) {
uint32_t ue_id = meas_value.ue_id;
value = (int32_t)enb_metrics.stack.mac.ues[ue_id].ul_rssi;
printf("extract last \"test\" value as int, (filled with UE_LEVEL metric: ul_rssi) value %i \n", value);
return true;
}
default:
return false;
}
}
// random_int: no_label
if (meas_value.name.c_str() == std::string("random_int")) {
switch (meas_value.label) {
case NO_LABEL:
value = srsran_random_uniform_int_dist(random_gen, 0, 100);
printf("extract last \"random_int\" value as int, random value %i \n", value);
return true;
default:
return false;
}
}
return false;
}
bool e2sm_kpm::_extract_real_type_meas_value(e2sm_kpm_meas_def_t& meas_value,
const enb_metrics_t& enb_metrics,
float& value)
{
// all real type measurements
// cpu0_load: no_label
if (meas_value.name.c_str() == std::string("cpu0_load")) {
switch (meas_value.label) {
case NO_LABEL:
value = enb_metrics.sys.cpu_load[0];
return true;
default:
return false;
}
}
// cpu_load: min,max,avg
if (meas_value.name.c_str() == std::string("cpu_load")) {
uint32_t size;
switch (meas_value.label) {
case MIN_LABEL:
value = *std::min_element(enb_metrics.sys.cpu_load.begin(), enb_metrics.sys.cpu_load.end());
return true;
case MAX_LABEL:
value = *std::max_element(enb_metrics.sys.cpu_load.begin(), enb_metrics.sys.cpu_load.end());
return true;
case AVG_LABEL:
size = enb_metrics.sys.cpu_load.size();
value = std::accumulate(enb_metrics.sys.cpu_load.begin(), enb_metrics.sys.cpu_load.end(), 0.0 / size);
return true;
default:
return false;
}
}
return false;
}

@ -0,0 +1,32 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#include "srsgnb/hdr/stack/ric/e2sm_kpm_common.h"
std::string e2sm_kpm_label_2_str(e2sm_kpm_label_enum label)
{
switch (label) {
case NO_LABEL:
return "NO_LABEL";
case MIN_LABEL:
return "MIN_LABEL";
case MAX_LABEL:
return "MAX_LABEL";
case AVG_LABEL:
return "AVG_LABEL";
case SUM_LABEL:
return "SUM_LABEL";
default:
return "UNKNOWN_LABEL";
}
}

@ -0,0 +1,585 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*
*/
#include "srsgnb/hdr/stack/ric/e2sm_kpm_report_service.h"
e2sm_kpm_report_service::e2sm_kpm_report_service(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition) :
parent(e2sm_kpm),
action_id(action_id),
action_def_generic(action_definition),
ric_ind_header_generic(),
ric_ind_header(ric_ind_header_generic.ind_hdr_formats.ind_hdr_format1()),
meas_collection_timer(parent->task_sched_ptr->get_unique_timer())
{
}
std::vector<e2sm_kpm_label_enum>
e2sm_kpm_report_service::_get_present_labels(const meas_info_item_s& action_meas_info_item)
{
std::vector<e2sm_kpm_label_enum> labels;
// TODO: add all labels defined in e2sm_kpm doc
for (uint32_t l = 0; l < action_meas_info_item.label_info_list.size(); l++) {
if (action_meas_info_item.label_info_list[l].meas_label.no_label_present) {
labels.push_back(NO_LABEL);
}
if (action_meas_info_item.label_info_list[l].meas_label.min_present) {
labels.push_back(MIN_LABEL);
}
if (action_meas_info_item.label_info_list[l].meas_label.max_present) {
labels.push_back(MAX_LABEL);
}
if (action_meas_info_item.label_info_list[l].meas_label.avg_present) {
labels.push_back(AVG_LABEL);
}
if (action_meas_info_item.label_info_list[l].meas_label.sum_present) {
labels.push_back(SUM_LABEL);
}
}
return labels;
}
bool e2sm_kpm_report_service::_initialize_ric_ind_hdr()
{
// TODO: set the remaining fields properly (they are optional)
ric_ind_header.collet_start_time.from_number(std::time(0));
// ric_ind_header.file_formatversion.from_string(hdr.file_formatversion);
// ric_ind_header.sender_name.from_string(hdr.sender_name);
// ric_ind_header.sender_type.from_string(hdr.sender_type);
// ric_ind_header.vendor_name.from_string(hdr.vendor_name);
return true;
}
meas_record_item_c::types e2sm_kpm_report_service::_get_meas_data_type(std::string meas_name,
e2sm_kpm_label_enum label,
meas_record_l& meas_record_list)
{
meas_record_item_c::types data_type = meas_record_item_c::types::options::nulltype;
// if no data collected check the type using metric definition
if (meas_record_list.size() == 0) {
e2sm_kpm_metric_t metric_definition;
if (not parent->_get_meas_definition(meas_name, metric_definition)) {
parent->logger.debug("No definition for measurement type \"%s\"", metric_definition.name);
return data_type;
}
if (metric_definition.data_type == INTEGER) {
data_type = meas_record_item_c::types::options::integer;
} else {
data_type = meas_record_item_c::types::options::real;
}
} else {
// check the data type of the first element in the list
data_type = meas_record_list[0].type();
}
return data_type;
}
bool e2sm_kpm_report_service::_start_meas_collection()
{
if (granul_period) {
printf("Start collecting measurements every %i ms\n", granul_period);
parent->logger.debug("Start collecting measurements every every %i ms", granul_period);
meas_collection_timer.set(granul_period, [this](uint32_t tid) { this->_collect_meas_data(); });
meas_collection_timer.run();
return true;
}
return false;
}
bool e2sm_kpm_report_service::stop()
{
return _stop_meas_collection();
}
bool e2sm_kpm_report_service::_stop_meas_collection()
{
if (meas_collection_timer.is_running()) {
printf("Stop collecting measurements every %i ms\n", granul_period);
parent->logger.debug("Stop collecting measurements every %i ms\n", granul_period);
meas_collection_timer.stop();
return true;
}
return false;
}
bool e2sm_kpm_report_service::_reschedule_meas_collection()
{
if (granul_period) {
meas_collection_timer.run();
return true;
}
return false;
}
e2sm_kpm_report_service_style1::e2sm_kpm_report_service_style1(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition) :
e2sm_kpm_report_service(e2sm_kpm, action_id, action_definition),
action_def(action_def_generic.action_definition_formats.action_definition_format1()),
ric_ind_message(ric_ind_message_generic.ind_msg_formats.set_ind_msg_format1())
{
ind_msg_format = e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types_opts::ind_msg_format1;
granul_period = action_def.granul_period;
cell_global_id_present = action_def.cell_global_id_present;
if (cell_global_id_present) {
cell_global_id = action_def.cell_global_id;
}
this->_initialize_ric_ind_hdr();
this->_initialize_ric_ind_msg();
granul_period = 1000; // TODO: overwrite for testing
_start_meas_collection();
}
bool e2sm_kpm_report_service_style1::_initialize_ric_ind_msg()
{
meas_info_list_l action_meas_info_list = action_def.meas_info_list;
// ric_ind_message.granul_period_present = true;
// ric_ind_message.granul_period = granul_period; // TODO: our asn1 has some issues with this field
ric_ind_message.granul_period = 0;
ric_ind_message.meas_info_list.resize(action_meas_info_list.size());
ric_ind_message.meas_data.resize(action_meas_info_list.size());
// add measurement info
for (uint32_t i = 0; i < ric_ind_message.meas_info_list.size(); i++) {
// structs to fill
meas_info_item_s& meas_info_item = ric_ind_message.meas_info_list[i];
// measurements definition
meas_info_item_s& meas_def_item = action_meas_info_list[i];
std::string meas_name = meas_def_item.meas_type.meas_name().to_string();
meas_info_item.meas_type.set_meas_name().from_string(meas_name.c_str());
meas_info_item.label_info_list.resize(meas_def_item.label_info_list.size());
// TODO: add all labels defined in e2sm_kpm doc, make this part generic and put to the base class
for (uint32_t l = 0; l < meas_def_item.label_info_list.size(); l++) {
if (meas_def_item.label_info_list[l].meas_label.no_label_present) {
meas_info_item.label_info_list[l].meas_label.no_label_present = true;
meas_info_item.label_info_list[l].meas_label.no_label = meas_label_s::no_label_opts::true_value;
}
if (meas_def_item.label_info_list[l].meas_label.min_present) {
meas_info_item.label_info_list[l].meas_label.min_present = true;
meas_info_item.label_info_list[l].meas_label.min = meas_label_s::min_opts::true_value;
}
if (meas_def_item.label_info_list[l].meas_label.max_present) {
meas_info_item.label_info_list[l].meas_label.max_present = true;
meas_info_item.label_info_list[l].meas_label.max = meas_label_s::max_opts::true_value;
}
if (meas_def_item.label_info_list[l].meas_label.avg_present) {
meas_info_item.label_info_list[l].meas_label.avg_present = true;
meas_info_item.label_info_list[l].meas_label.avg = meas_label_s::avg_opts::true_value;
}
if (meas_def_item.label_info_list[l].meas_label.sum_present) {
meas_info_item.label_info_list[l].meas_label.sum_present = true;
meas_info_item.label_info_list[l].meas_label.sum = meas_label_s::sum_opts::true_value;
}
}
}
return true;
}
bool e2sm_kpm_report_service_style1::process_ric_action_definition(e2sm_kpm* e2sm_kpm,
e2_sm_kpm_action_definition_s& action_def_generic)
{
e2_sm_kpm_action_definition_format1_s& action_definition =
action_def_generic.action_definition_formats.action_definition_format1();
bool cell_global_id_present = false;
uint64_t granul_period;
uint64_t eutra_cell_id;
uint64_t plmn_id;
ueid_c ue_id;
meas_info_list_l meas_info_list;
granul_period = action_definition.granul_period;
if (granul_period == 0) {
e2sm_kpm->logger.debug("Action granularity period of %i is not supported -> do not admitted action\n",
granul_period);
return false;
}
if (action_definition.cell_global_id_present) {
cell_global_id_present = true;
if (action_definition.cell_global_id.type() == cgi_c::types_opts::eutra_cgi) {
eutra_cell_id = action_definition.cell_global_id.eutra_cgi().eutra_cell_id.to_number();
plmn_id = action_definition.cell_global_id.eutra_cgi().plmn_id.to_number();
e2sm_kpm->logger.debug("plmn_id 0x%x, eutra_cell_id %i", plmn_id, eutra_cell_id);
// TODO: check if E2 node has cell_id and plmn_id
}
}
std::map<std::string, e2sm_kpm_label_enum> admitted_metrics;
meas_info_list = action_definition.meas_info_list;
for (uint32_t i = 0; i < meas_info_list.size(); i++) {
std::string meas_name = meas_info_list[i].meas_type.meas_name().to_string();
e2sm_kpm_metric_t metric_definition;
if (not e2sm_kpm->_get_meas_definition(meas_name, metric_definition)) {
printf("Unsupported measurement name: \"%s\" --> do not admit action\n", meas_name.c_str());
return false;
}
uint32_t nof_labels = 0;
// TODO: add all labels defined in e2sm_kpm doc, make this part generic and put to base class
// TODO: check if metric is supported at the requested level, i.e., CELL_LEVEL if cell_global_id_present == true,
// and ENB_LEVEL otherwise
for (uint32_t l = 0; l < meas_info_list[i].label_info_list.size(); l++) {
if (meas_info_list[i].label_info_list[l].meas_label.no_label_present) {
if (metric_definition.supported_labels & NO_LABEL) {
nof_labels++;
admitted_metrics[meas_name] = NO_LABEL;
} else {
printf("Unsupported label: NO_LABEL for metric \"%s\" --> do not admit action\n", meas_name.c_str());
return false;
}
}
if (meas_info_list[i].label_info_list[l].meas_label.min_present) {
if (metric_definition.supported_labels & MIN_LABEL) {
nof_labels++;
admitted_metrics[meas_name] = MIN_LABEL;
} else {
printf("Unsupported label: MIN_LABEL for metric \"%s\" --> do not admit action\n", meas_name.c_str());
return false;
}
}
if (meas_info_list[i].label_info_list[l].meas_label.max_present) {
if (metric_definition.supported_labels & MAX_LABEL) {
nof_labels++;
admitted_metrics[meas_name] = MAX_LABEL;
} else {
printf("Unsupported label: MAX_LABEL for metric \"%s\" --> do not admit action\n", meas_name.c_str());
return false;
}
}
if (meas_info_list[i].label_info_list[l].meas_label.avg_present) {
if (metric_definition.supported_labels & AVG_LABEL) {
nof_labels++;
admitted_metrics[meas_name] = AVG_LABEL;
} else {
printf("Unsupported label: AVG_LABEL for metric \"%s\" --> do not admit action\n", meas_name.c_str());
return false;
}
}
}
// Note: currently we use labels as choice (i.e., only one can be present) as documentation is not clear about it
if (nof_labels > 1) {
printf("Only one label per metric can be present, meas: \"%s\" has %i labels --> do not admit action\n",
meas_name.c_str(),
nof_labels);
return false;
}
}
printf("Admitted action with the following metrics and labels: \n");
for (const auto& it : admitted_metrics) {
std::string meas_name = it.first;
std::string label_str = e2sm_kpm_label_2_str(it.second);
printf("--- Metric: \"%s\" with label: %s\n", meas_name.c_str(), label_str.c_str());
}
return true;
}
meas_data_item_s& e2sm_kpm_report_service_style1::_get_meas_data_item(std::string meas_name,
e2sm_kpm_label_enum label,
uint32_t ue_id,
bool& ref_found)
{
meas_info_list_l& meas_info_list = ric_ind_message.meas_info_list;
ref_found = false;
// find proper index of the metric
for (uint32_t i = 0; i < meas_info_list.size(); i++) {
// measurements definition
meas_info_item_s meas_def_item = meas_info_list[i];
std::string meas_def_name = meas_def_item.meas_type.meas_name().to_string();
// check if the metric name matches
if (meas_def_name != meas_name.c_str()) {
continue;
}
// check if the metric label matches
// TODO: add all labels defined in e2sm_kpm doc
for (uint32_t l = 0; l < meas_def_item.label_info_list.size(); l++) {
if (meas_def_item.label_info_list[l].meas_label.no_label_present and label == NO_LABEL) {
ref_found = true;
return ric_ind_message.meas_data[i];
}
if (meas_def_item.label_info_list[l].meas_label.min_present and label == MIN_LABEL) {
ref_found = true;
return ric_ind_message.meas_data[i];
}
if (meas_def_item.label_info_list[l].meas_label.max_present and label == MAX_LABEL) {
ref_found = true;
return ric_ind_message.meas_data[i];
}
if (meas_def_item.label_info_list[l].meas_label.avg_present and label == AVG_LABEL) {
ref_found = true;
return ric_ind_message.meas_data[i];
}
if (meas_def_item.label_info_list[l].meas_label.sum_present and label == SUM_LABEL) {
ref_found = true;
return ric_ind_message.meas_data[i];
}
}
}
// TODO assert if match == false, has to be present as was created during initialization
ref_found = false;
return ric_ind_message.meas_data[0];
}
bool e2sm_kpm_report_service_style1::_collect_meas_data()
{
meas_info_list_l& meas_info_list = ric_ind_message.meas_info_list;
for (uint32_t i = 0; i < meas_info_list.size(); i++) {
meas_info_item_s& meas_def_item = meas_info_list[i];
std::string meas_name = meas_def_item.meas_type.meas_name().to_string();
std::vector<e2sm_kpm_label_enum> labels = _get_present_labels(meas_def_item);
for (const auto& label : labels) {
// TODO: probably some labels need a special processing (e.g., use bin width that needs to be stored)
// get a proper record list
bool ref_found = false;
meas_data_item_s& meas_data_item = _get_meas_data_item(meas_name, label, 0, ref_found);
if (not ref_found) {
parent->logger.info("Cannot find a meas record list, action_id %i, metric \"%s\" label: %i",
action_id,
meas_name.c_str(),
label);
return false;
}
// get data type
meas_record_item_c::types data_type = _get_meas_data_type(meas_name, label, meas_data_item.meas_record);
// extract a needed value from enb metrics and add to the proper meas record list
e2sm_kpm_meas_def_t meas_value;
meas_value.name = meas_name;
meas_value.label = label;
meas_value.scope = ENB_LEVEL;
if (cell_global_id_present) {
meas_value.scope = CELL_LEVEL;
meas_value.cell_id = 0;
}
meas_value.data_type = data_type;
meas_record_item_c item;
if (not parent->_collect_meas_value(meas_value, item)) {
parent->logger.info("Cannot extract value \"%s\" label: %i", meas_name.c_str(), label);
return false;
}
// save meas value in the proper record list
meas_data_item.meas_record.push_back(item);
}
}
// reschedule measurement collection
_reschedule_meas_collection();
return true;
}
bool e2sm_kpm_report_service_style1::is_ric_ind_ready()
{
// TODO: check if only NO_VALUES, if so then skip
return true;
}
bool e2sm_kpm_report_service_style1::clear_collected_data()
{
ric_ind_header.collet_start_time.from_number(std::time(0));
for (uint32_t i = 0; i < ric_ind_message.meas_data.size(); ++i) {
ric_ind_message.meas_data[i].meas_record.clear();
}
return true;
}
e2sm_kpm_report_service_style2::e2sm_kpm_report_service_style2(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition) :
e2sm_kpm_report_service(e2sm_kpm, action_id, action_definition),
action_def(action_def_generic.action_definition_formats.action_definition_format2()),
ric_ind_message(ric_ind_message_generic.ind_msg_formats.set_ind_msg_format1())
{
ind_msg_format = e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types_opts::ind_msg_format1;
this->_initialize_ric_ind_hdr();
this->_initialize_ric_ind_msg();
}
bool e2sm_kpm_report_service_style2::_initialize_ric_ind_msg()
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style2::process_ric_action_definition(e2sm_kpm* e2sm_kpm,
e2_sm_kpm_action_definition_s& action_def_generic)
{
// TODO: implement
// note: similar to e2sm_kpm_report_service_style1::process_ric_action_definition but in addition
// we need to check whether measurement is supported at UE_LEVEL
return false;
}
bool e2sm_kpm_report_service_style2::_collect_meas_data()
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style2::is_ric_ind_ready()
{
// TODO: check if only NO_VALUES, if so then skip
return false;
}
bool e2sm_kpm_report_service_style2::clear_collected_data()
{
// TODO: implement
return false;
}
e2sm_kpm_report_service_style3::e2sm_kpm_report_service_style3(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition) :
e2sm_kpm_report_service(e2sm_kpm, action_id, action_definition),
action_def(action_def_generic.action_definition_formats.action_definition_format3()),
ric_ind_message(ric_ind_message_generic.ind_msg_formats.set_ind_msg_format2())
{
ind_msg_format = e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types_opts::ind_msg_format1;
this->_initialize_ric_ind_hdr();
this->_initialize_ric_ind_msg();
}
bool e2sm_kpm_report_service_style3::_initialize_ric_ind_msg()
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style3::process_ric_action_definition(e2sm_kpm* e2sm_kpm,
e2_sm_kpm_action_definition_s& action_def_generic)
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style3::_collect_meas_data()
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style3::is_ric_ind_ready()
{
// TODO: check if only NO_VALUES, if so then skip
return false;
}
bool e2sm_kpm_report_service_style3::clear_collected_data()
{
// TODO: implement
return false;
}
e2sm_kpm_report_service_style4::e2sm_kpm_report_service_style4(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition) :
e2sm_kpm_report_service(e2sm_kpm, action_id, action_definition),
action_def(action_def_generic.action_definition_formats.action_definition_format4()),
ric_ind_message(ric_ind_message_generic.ind_msg_formats.set_ind_msg_format3())
{
ind_msg_format = e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types_opts::ind_msg_format1;
this->_initialize_ric_ind_hdr();
this->_initialize_ric_ind_msg();
}
bool e2sm_kpm_report_service_style4::_initialize_ric_ind_msg()
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style4::process_ric_action_definition(e2sm_kpm* e2sm_kpm,
e2_sm_kpm_action_definition_s& action_def_generic)
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style4::_collect_meas_data()
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style4::is_ric_ind_ready()
{
// TODO: check if only NO_VALUES, if so then skip
return false;
}
bool e2sm_kpm_report_service_style4::clear_collected_data()
{
// TODO: implement
return false;
}
e2sm_kpm_report_service_style5::e2sm_kpm_report_service_style5(e2sm_kpm* e2sm_kpm,
uint16_t action_id,
e2_sm_kpm_action_definition_s action_definition) :
e2sm_kpm_report_service(e2sm_kpm, action_id, action_definition),
action_def(action_def_generic.action_definition_formats.action_definition_format5()),
ric_ind_message(ric_ind_message_generic.ind_msg_formats.set_ind_msg_format3())
{
ind_msg_format = e2_sm_kpm_ind_msg_s::ind_msg_formats_c_::types_opts::ind_msg_format1;
this->_initialize_ric_ind_hdr();
this->_initialize_ric_ind_msg();
}
bool e2sm_kpm_report_service_style5::_initialize_ric_ind_msg()
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style5::process_ric_action_definition(e2sm_kpm* e2sm_kpm,
e2_sm_kpm_action_definition_s& action_def_generic)
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style5::_collect_meas_data()
{
// TODO: implement
return false;
}
bool e2sm_kpm_report_service_style5::is_ric_ind_ready()
{
// TODO: check if only NO_VALUES, if so then skip
return false;
}
bool e2sm_kpm_report_service_style5::clear_collected_data()
{
// TODO: implement
return false;
}

@ -0,0 +1,12 @@
#
# Copyright 2013-2023 Software Radio Systems Limited
#
# By using this file, you agree to the terms and conditions set
# forth in the LICENSE file which can be found at the top level of
# the distribution.
#
add_executable(e2ap_test e2ap_test.cc)
target_link_libraries(e2ap_test srsran_common ric_e2 srsgnb_ric srsenb_upper srsgnb_stack ${SCTP_LIBRARIES})
add_test(e2ap_test e2ap_test)

@ -0,0 +1,177 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2023 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsgnb/hdr/stack/ric/e2ap.h"
#include "srsran/asn1/e2ap.h"
#include "srsran/common/test_common.h"
#include "srsran/interfaces/e2_metrics_interface.h"
#include "srsran/interfaces/enb_metrics_interface.h"
#include "srsran/srsran.h"
class dummy_metrics_interface : public srsenb::e2_interface_metrics
{
bool pull_metrics(srsenb::enb_metrics_t* m) { return true; }
bool register_e2sm(e2sm* sm) { return true; }
bool unregister_e2sm(e2sm* sm) { return true; }
};
// function to test the encoding of the E2AP message
void test_reference_e2ap_setup_request()
{
uint8_t e2ap_msg_foreign[] = {
0x00, 0x01, 0x00, 0x80, 0xa3, 0x00, 0x00, 0x04, 0x00, 0x31, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 0x00,
0x05, 0xf5, 0x10, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0a, 0x00, 0x53, 0x00, 0x00, 0x08, 0x00, 0x4e, 0x00, 0x00,
0x93, 0x38, 0x00, 0x30, 0x4f, 0x52, 0x41, 0x4e, 0x2d, 0x45, 0x32, 0x53, 0x4d, 0x2d, 0x4b, 0x50, 0x4d, 0x00, 0x00,
0x18, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x35, 0x33, 0x31, 0x34, 0x38, 0x2e,
0x31, 0x2e, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x05, 0x00, 0x4b, 0x50, 0x4d, 0x20, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f,
0x72, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x4f, 0x52, 0x41, 0x4e, 0x2d, 0x45, 0x32, 0x53, 0x4d, 0x2d, 0x4b, 0x50, 0x4d,
0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x01, 0x80, 0x44, 0x55, 0x4d, 0x4d, 0x59, 0x20,
0x4f, 0x41, 0x49, 0x2d, 0x41, 0x4d, 0x46, 0x00, 0x0c, 0x46, 0x41, 0x4b, 0x45, 0x20, 0x52, 0x45, 0x51, 0x55, 0x45,
0x53, 0x54, 0x0d, 0x46, 0x41, 0x4b, 0x45, 0x20, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45};
asn1::cbit_ref bref(&e2ap_msg_foreign[0], sizeof(e2ap_msg_foreign));
e2_ap_pdu_c pdu;
asn1::SRSASN_CODE unpack_ret = pdu.unpack(bref);
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
printf("Unpacked E2AP PDU %d\n", (int)unpack_ret);
auto& ran_func_data = pdu.init_msg()
.value.e2setup_request()
->ra_nfunctions_added.value[0]
.value()
.ra_nfunction_item()
.ran_function_definition;
srsran::byte_buffer_t ran_function_def;
asn1::cbit_ref ran_func_bref(ran_function_def.msg, ran_function_def.get_tailroom());
std::copy(ran_func_data.data(), ran_func_data.data() + ran_func_data.size(), ran_function_def.begin());
e2_sm_kpm_ra_nfunction_description_s e2sm_kpm_ra_nfunction_description;
asn1::SRSASN_CODE nfunc_unpack = e2sm_kpm_ra_nfunction_description.unpack(ran_func_bref);
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, nfunc_unpack);
printf("Unpacked E2SM PDU (KPM RAN function description) %d\n", (int)nfunc_unpack);
}
void test_native_e2ap_setup_request()
{
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
e2_ap_pdu_c pdu, pdu2;
srslog::basic_logger& logger = srslog::fetch_basic_logger("E2AP");
dummy_metrics_interface dummy_metrics;
e2ap e2ap_(logger, nullptr, &dummy_metrics, NULL);
pdu = e2ap_.generate_setup_request();
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
if (pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
printf("Failed to pack TX E2 PDU\n");
return;
}
asn1::cbit_ref bref2(buf->msg, buf->get_tailroom());
asn1::SRSASN_CODE unpack_ret = pdu2.unpack(bref2);
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
printf("Unpacked native E2AP PDU %d\n", (int)unpack_ret);
}
void test_reference_e2ap_subscription_request()
{
uint8_t e2ap_msg_foreign[] = {0x00, 0x08, 0x40, 0x2b, 0x00, 0x00, 0x03, 0x00, 0x1d, 0x00, 0x05, 0x00,
0x00, 0x7b, 0x00, 0x15, 0x00, 0x05, 0x00, 0x02, 0x00, 0x01, 0x00, 0x1e,
0x00, 0x15, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x13, 0x40,
0x0a, 0x60, 0x01, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, 0x02, 0x00};
asn1::cbit_ref bref(&e2ap_msg_foreign[0], sizeof(e2ap_msg_foreign));
e2_ap_pdu_c pdu;
asn1::SRSASN_CODE unpack_ret = pdu.unpack(bref);
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
printf("Unpacked E2AP PDU (subscription request) %d\n", (int)unpack_ret);
}
void test_native_e2ap_subscription_response()
{
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
e2_ap_pdu_c pdu, pdu2;
srslog::basic_logger& logger = srslog::fetch_basic_logger("E2AP");
dummy_metrics_interface dummy_metrics;
e2ap e2ap_(logger, nullptr, &dummy_metrics, NULL);
ric_subscription_reponse_t ric_subscription_reponse;
ric_subscription_reponse.ric_requestor_id = 1021;
ric_subscription_reponse.ric_instance_id = 0;
ric_subscription_reponse.ra_nfunction_id = 147;
ric_subscription_reponse.admitted_actions.push_back(0);
pdu = e2ap_.generate_subscription_response(ric_subscription_reponse);
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
if (pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
printf("Failed to pack TX E2 PDU\n");
return;
}
asn1::cbit_ref bref2(buf->msg, buf->get_tailroom());
asn1::SRSASN_CODE unpack_ret = pdu2.unpack(bref2);
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
printf("Unpacked native E2AP PDU (subscription response) %d\n", (int)unpack_ret);
}
void test_native_e2ap_reset_request()
{
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
e2_ap_pdu_c pdu, pdu2;
srslog::basic_logger& logger = srslog::fetch_basic_logger("E2AP");
dummy_metrics_interface dummy_metrics;
e2ap e2ap_(logger, nullptr, &dummy_metrics, NULL);
pdu = e2ap_.generate_reset_request();
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
if (pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
printf("Failed to pack TX E2 PDU\n");
return;
}
asn1::cbit_ref bref2(buf->msg, buf->get_tailroom());
asn1::SRSASN_CODE unpack_ret = pdu2.unpack(bref2);
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
printf("Unpacked native E2AP PDU RESET %d\n", (int)unpack_ret);
}
void test_native_e2ap_reset_response()
{
srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer();
e2_ap_pdu_c pdu, pdu2;
srslog::basic_logger& logger = srslog::fetch_basic_logger("E2AP");
dummy_metrics_interface dummy_metrics;
e2ap e2ap_(logger, nullptr, &dummy_metrics, NULL);
pdu = e2ap_.generate_reset_response();
asn1::bit_ref bref(buf->msg, buf->get_tailroom());
if (pdu.pack(bref) != asn1::SRSASN_SUCCESS) {
printf("Failed to pack TX E2 PDU\n");
return;
}
asn1::cbit_ref bref2(buf->msg, buf->get_tailroom());
asn1::SRSASN_CODE unpack_ret = pdu2.unpack(bref2);
TESTASSERT_EQ(asn1::SRSASN_SUCCESS, unpack_ret);
printf("Unpacked native E2AP PDU RESET RESPONSE %d\n", (int)unpack_ret);
}
// add tets for set-up request and response
int main()
{
test_reference_e2ap_setup_request();
test_native_e2ap_setup_request();
test_reference_e2ap_subscription_request();
test_native_e2ap_subscription_response();
test_native_e2ap_reset_request();
test_native_e2ap_reset_response();
// call reset test functions here
return 0;
}

@ -428,25 +428,21 @@ void fill_nzp_csi_rs_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_m
void fill_csi_resource_cfg_to_add(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg)
{
if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) {
csi_meas_cfg.csi_res_cfg_to_add_mod_list.resize(3);
csi_meas_cfg.csi_res_cfg_to_add_mod_list.resize(2);
csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].csi_res_cfg_id = 0;
auto& nzp = csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].csi_rs_res_set_list.set_nzp_csi_rs_ssb();
auto& res0 = csi_meas_cfg.csi_res_cfg_to_add_mod_list[0];
res0.csi_res_cfg_id = 0;
res0.bwp_id = 0;
res0.res_type.value = csi_res_cfg_s::res_type_opts::periodic;
auto& nzp = res0.csi_rs_res_set_list.set_nzp_csi_rs_ssb();
nzp.nzp_csi_rs_res_set_list.push_back(0);
csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].bwp_id = 0;
csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].res_type.value = csi_res_cfg_s::res_type_opts::periodic;
csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].csi_res_cfg_id = 1;
auto& im_res = csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].csi_rs_res_set_list.set_csi_im_res_set_list();
im_res.push_back(0);
csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].bwp_id = 0;
csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].res_type.value = csi_res_cfg_s::res_type_opts::periodic;
csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].csi_res_cfg_id = 2;
auto& nzp2 = csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].csi_rs_res_set_list.set_nzp_csi_rs_ssb();
auto& res2 = csi_meas_cfg.csi_res_cfg_to_add_mod_list[1];
res2.csi_res_cfg_id = 1;
res2.bwp_id = 0;
res2.res_type.value = csi_res_cfg_s::res_type_opts::periodic;
auto& nzp2 = res2.csi_rs_res_set_list.set_nzp_csi_rs_ssb();
nzp2.nzp_csi_rs_res_set_list.push_back(1);
csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].bwp_id = 0;
csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].res_type.value = csi_res_cfg_s::res_type_opts::periodic;
}
}
@ -498,9 +494,6 @@ int fill_csi_meas_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas
fill_nzp_csi_rs_from_enb_cfg(cfg, csi_meas_cfg);
if (cfg.is_standalone) {
// CSI IM config
fill_csi_im_resource_cfg_to_add(cfg, csi_meas_cfg);
// CSI report config
fill_csi_report_from_enb_cfg(cfg, csi_meas_cfg);
}
@ -1357,7 +1350,7 @@ int fill_cellgroup_with_radio_bearer_cfg(const rrc_nr_cfg_t&
// Add DRBs
for (const drb_to_add_mod_s& drb : bearers.drb_to_add_mod_list) {
out.rlc_bearer_to_add_mod_list.push_back({});
uint32_t lcid = drb.drb_id + (int)srsran::nr_srb::count - 1;
uint32_t lcid = drb.drb_id + srsran::MAX_NR_SRB_ID;
enb_bearer_manager::radio_bearer_t rb = bearer_mapper.get_lcid_bearer(rnti, lcid);
if (rb.is_valid() and cfg.five_qi_cfg.find(rb.five_qi) != cfg.five_qi_cfg.end()) {
fill_drb(cfg, rb, (srsran::nr_drb)drb.drb_id, out.rlc_bearer_to_add_mod_list.back());

@ -1349,29 +1349,36 @@ void rrc_nr::ue::establish_eps_bearer(uint32_t pdu_session_id,
nas_pdu_queue.push_back(std::move(pdu));
// Add SRB2, if not yet added
if (radio_bearer_cfg.srb_to_add_mod_list.size() <= 1) {
asn1::rrc_nr::srb_to_add_mod_s* srb_it =
std::find_if(radio_bearer_cfg.srb_to_add_mod_list.begin(),
radio_bearer_cfg.srb_to_add_mod_list.end(),
[](const asn1::rrc_nr::srb_to_add_mod_s& srb) { return srb.srb_id == 2; });
if (srb_it == radio_bearer_cfg.srb_to_add_mod_list.end()) {
next_radio_bearer_cfg.srb_to_add_mod_list.push_back(srb_to_add_mod_s{});
next_radio_bearer_cfg.srb_to_add_mod_list.back().srb_id = 2;
}
drb_to_add_mod_s drb;
drb.cn_assoc_present = true;
drb.cn_assoc.set_sdap_cfg().pdu_session = 1;
drb.cn_assoc.set_sdap_cfg().pdu_session = pdu_session_id;
drb.cn_assoc.sdap_cfg().sdap_hdr_dl.value = asn1::rrc_nr::sdap_cfg_s::sdap_hdr_dl_opts::absent;
drb.cn_assoc.sdap_cfg().sdap_hdr_ul.value = asn1::rrc_nr::sdap_cfg_s::sdap_hdr_ul_opts::absent;
drb.cn_assoc.sdap_cfg().default_drb = true;
drb.cn_assoc.sdap_cfg().mapped_qos_flows_to_add.resize(1);
drb.cn_assoc.sdap_cfg().mapped_qos_flows_to_add[0] = 1;
drb.drb_id = 1;
drb.drb_id = lcid - srsran::MAX_NR_SRB_ID;
drb.pdcp_cfg_present = true;
drb.pdcp_cfg = parent->cfg.five_qi_cfg[five_qi].pdcp_cfg;
next_radio_bearer_cfg.drb_to_add_mod_list.push_back(drb);
parent->bearer_mapper->add_eps_bearer(
rnti, lcid - 3, srsran::srsran_rat_t::nr, lcid); // TODO: configurable bearer id <-> lcid mapping
parent->bearer_mapper->set_five_qi(rnti, lcid - 3, five_qi);
parent->bearer_mapper->add_eps_bearer(rnti,
pdu_session_id,
srsran::srsran_rat_t::nr,
lcid); // TODO: configurable bearer id <-> lcid mapping
parent->bearer_mapper->set_five_qi(rnti, pdu_session_id, five_qi);
// store 5QI for possible reestablishment of DRB
drb1_five_qi = five_qi;

@ -146,7 +146,7 @@ void test_rrc_sa_ngap_integration(ngap_args_t ngap_args)
srsran::socket_manager rx_sockets;
srsenb::ngap ngap_obj(&task_sched, ngap_logger, &rx_sockets);
srsenb::gtpu gtpu_obj(&task_sched, gtpu_logger, &rx_sockets);
srsenb::gtpu gtpu_obj(&task_sched, gtpu_logger, srsran::srsran_rat_t::nr, &rx_sockets);
gtpu_args_t gtpu_args;
gtpu_args.embms_enable = false;

@ -345,6 +345,7 @@ private:
bool have_mtch_stop = false;
std::mutex mtch_mutex;
std::mutex mch_mutex;
std::condition_variable mtch_cvar;
std::atomic<bool> is_pending_tx_end{false};

@ -36,6 +36,8 @@ struct rrc_args_t {
std::array<uint8_t, SRSRAN_RRC_N_BANDS> supported_bands;
std::vector<uint32_t> supported_bands_nr;
uint32_t nof_supported_bands;
uint32_t nof_lte_carriers;
uint32_t nof_nr_carriers;
bool support_ca;
int mbms_service_id;
uint32_t mbms_service_port;

@ -313,14 +313,14 @@ bool phy::cell_select(phy_cell_t cell)
// Update PCI before starting the background command to make sure PRACH gets the updated value
selected_cell.id = cell.pci;
// Update EARCN before starting the background task to make sure is taken into account when finding carriers to
// Update EARFCN before starting the background task to make sure is taken into account when finding carriers to
// measure inter-frequency neighbours (see set_cells_to_meas)
selected_earfcn = cell.earfcn;
// Indicate workers that cell selection is in progress
common.cell_is_selecting = true;
// Update EARCN before starting the background task to make sure is taken into account when finding carriers to
// Update EARFCN before starting the background task to make sure is taken into account when finding carriers to
// measure inter-frequency neighbours (see set_cells_to_meas)
selected_earfcn = cell.earfcn;

@ -953,6 +953,7 @@ void phy_common::reset()
void phy_common::build_mch_table()
{
// First reset tables
std::lock_guard<std::mutex> lock(mch_mutex);
bzero(&mch_table[0], sizeof(uint8_t) * 40);
// 40 element table represents 4 frames (40 subframes)
@ -975,6 +976,7 @@ void phy_common::build_mch_table()
void phy_common::build_mcch_table()
{
std::lock_guard<std::mutex> lock(mch_mutex);
// First reset tables
bzero(&mcch_table[0], sizeof(uint8_t) * 10);
generate_mcch_table(&mcch_table[0], (uint32_t)mbsfn_config.mbsfn_area_info.mcch_cfg.sf_alloc_info);
@ -1032,7 +1034,7 @@ bool phy_common::is_mch_subframe(srsran_mbsfn_cfg_t* cfg, uint32_t phy_tti)
cfg->mbsfn_mcs = 2;
cfg->enable = false;
cfg->is_mcch = false;
std::lock_guard<std::mutex> lock(mch_mutex);
// Check for MCCH
if (is_mcch_subframe(cfg, phy_tti)) {
cfg->is_mcch = true;

@ -1,47 +0,0 @@
/**
* Copyright 2013-2022 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsRAN is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main(int argc, char* argv[])
{
if (argc != 2) {
std::cout << "Please call with the binary to provide net admin capabilities to as a parameter." << std::endl;
std::cout << "E.g. ./set_net_admin_caps myprogCalling " << std::endl;
return -1;
}
std::string command("setcap 'cap_net_admin=eip' ");
command += argv[1];
std::cout << "Calling " << command << " with root rights." << std::endl;
setuid(0);
system(command.c_str());
return 0;
}

@ -2001,8 +2001,11 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry)
phy_layer_params_v1020.multi_cluster_pusch_within_cc_r10_present = false;
phy_layer_params_v1020.non_contiguous_ul_ra_within_cc_list_r10_present = false;
rf_params_v1020_s rf_params;
band_combination_params_r10_l combination_params;
if (args.support_ca) {
// add Intraband Contiguous or Interband Non-contiguous CA band combination
// note that nof_supported_bands=1 when all cells are in the same but non-contiguous band
for (uint32_t k = 0; k < args.nof_supported_bands; k++) {
ca_mimo_params_dl_r10_s ca_mimo_params_dl;
ca_mimo_params_dl.ca_bw_class_dl_r10 = ca_bw_class_r10_e::f;
@ -2022,10 +2025,35 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry)
combination_params.push_back(band_params);
}
}
rf_params_v1020_s rf_params;
rf_params.supported_band_combination_r10.push_back(combination_params);
// add all 2CC, 3CC and 4CC Intraband Non-contiguous CA band combinations
for (uint32_t k = 0; k < args.nof_supported_bands; k++) {
for (uint32_t j = 2; j <= args.nof_lte_carriers; j++) {
combination_params.clear();
ca_mimo_params_dl_r10_s ca_mimo_params_dl;
ca_mimo_params_dl.ca_bw_class_dl_r10 = ca_bw_class_r10_e::a;
ca_mimo_params_dl.supported_mimo_cap_dl_r10_present = false;
ca_mimo_params_ul_r10_s ca_mimo_params_ul;
ca_mimo_params_ul.ca_bw_class_ul_r10 = ca_bw_class_r10_e::a;
ca_mimo_params_ul.supported_mimo_cap_ul_r10_present = false;
band_params_r10_s band_params;
band_params.band_eutra_r10 = args.supported_bands[k];
band_params.band_params_dl_r10_present = true;
band_params.band_params_dl_r10.push_back(ca_mimo_params_dl);
band_params.band_params_ul_r10_present = true;
band_params.band_params_ul_r10.push_back(ca_mimo_params_ul);
for (uint32_t l = 0; l < j; l++) {
combination_params.push_back(band_params);
}
rf_params.supported_band_combination_r10.push_back(combination_params);
}
}
ue_eutra_cap_v1020_ies_s cap_v1020;
if (args.ue_category >= 6 && args.ue_category <= 8) {
cap_v1020.ue_category_v1020_present = true;
@ -2196,7 +2224,7 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry)
}
// Pack caps and copy to cap info
uint8_t buf[64] = {};
uint8_t buf[128] = {};
asn1::bit_ref bref(buf, sizeof(buf));
if (cap.pack(bref) != asn1::SRSASN_SUCCESS) {
logger.error("Error packing EUTRA capabilities");

@ -277,6 +277,8 @@ int ue::parse_args(const all_args_t& args_)
args.stack.rrc.ue_category = (uint32_t)strtoul(args.stack.rrc.ue_category_str.c_str(), nullptr, 10);
// Consider Carrier Aggregation support if more than one
args.stack.rrc.nof_lte_carriers = args.phy.nof_lte_carriers;
args.stack.rrc.nof_nr_carriers = args.phy.nof_nr_carriers;
args.stack.rrc.support_ca = (args.phy.nof_lte_carriers > 1);
// Make sure fix sampling rate is set for SA mode

Loading…
Cancel
Save