added the handling of ho preparation failure and an ue class to s1ap

master
Francisco Paisana 5 years ago
parent 57cd40ca31
commit 50ed2ccfec

@ -938,7 +938,6 @@ LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellidentity(LIBLTE_S1AP_CELLIDENTITY_STRUCT*
for (i = 0; i < LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN; i++) {
liblte_value_2_bits(ie->buffer[i], ptr, 1);
}
liblte_align_up_zero(ptr, 8);
err = LIBLTE_SUCCESS;
}
return err;
@ -955,7 +954,6 @@ LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellidentity(uint8_t** ptr, LIBLTE_S1AP_CEL
for (i = 0; i < LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN; i++) {
ie->buffer[i] = liblte_bits_2_value(ptr, 1);
}
liblte_align_up(ptr, 8);
err = LIBLTE_SUCCESS;
}
return err;
@ -1494,7 +1492,6 @@ LIBLTE_ERROR_ENUM liblte_s1ap_pack_macroenb_id(LIBLTE_S1AP_MACROENB_ID_STRUCT* i
for (i = 0; i < LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN; i++) {
liblte_value_2_bits(ie->buffer[i], ptr, 1);
}
liblte_align_up_zero(ptr, 8);
err = LIBLTE_SUCCESS;
}
return err;
@ -1511,7 +1508,6 @@ LIBLTE_ERROR_ENUM liblte_s1ap_unpack_macroenb_id(uint8_t** ptr, LIBLTE_S1AP_MACR
for (i = 0; i < LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN; i++) {
ie->buffer[i] = liblte_bits_2_value(ptr, 1);
}
liblte_align_up(ptr, 8);
err = LIBLTE_SUCCESS;
}
return err;
@ -3735,7 +3731,7 @@ LIBLTE_ERROR_ENUM liblte_s1ap_pack_time_ue_stayedincell(LIBLTE_S1AP_TIME_UE_STAY
// Integer - ie->Time_UE_StayedInCell
// lb:0, ub:4095
liblte_align_up_zero(ptr, 8);
liblte_value_2_bits(0, ptr, (1 * 8) - 12);
liblte_value_2_bits(0, ptr, (2 * 8) - 12);
liblte_value_2_bits(ie->Time_UE_StayedInCell, ptr, 12);
liblte_align_up_zero(ptr, 8);
err = LIBLTE_SUCCESS;
@ -14603,6 +14599,7 @@ LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourceenb_totargetenb_transparentcontainer(
liblte_value_2_bits(ie->e_RABInformationList_present ? 1 : 0, ptr, 1);
liblte_value_2_bits(ie->subscriberProfileIDforRFP_present ? 1 : 0, ptr, 1);
liblte_value_2_bits(ie->iE_Extensions_present ? 1 : 0, ptr, 1);
liblte_align_up_zero(ptr, 8);
if (liblte_s1ap_pack_rrc_container(&ie->rRC_Container, ptr) != LIBLTE_SUCCESS) {
return LIBLTE_ERROR_ENCODE_FAIL;
@ -14655,6 +14652,7 @@ LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourceenb_totargetenb_transparentcontainer(
ie->e_RABInformationList_present = liblte_bits_2_value(ptr, 1);
ie->subscriberProfileIDforRFP_present = liblte_bits_2_value(ptr, 1);
ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1);
liblte_align_up(ptr, 8);
if (liblte_s1ap_unpack_rrc_container(ptr, &ie->rRC_Container) != LIBLTE_SUCCESS) {
return LIBLTE_ERROR_DECODE_FAIL;

@ -347,7 +347,7 @@ public:
};
private:
std::map<uint16_t, ue> users;
std::map<uint16_t, std::unique_ptr<ue> > users; // NOTE: has to have fixed addr
std::map<uint32_t, LIBLTE_S1AP_UEPAGINGID_STRUCT> pending_paging;

@ -31,8 +31,9 @@
#include "srslte/interfaces/enb_interfaces.h"
#include "common_enb.h"
#include "srslte/asn1/liblte_s1ap.h"
#include "s1ap_metrics.h"
#include "srslte/asn1/liblte_s1ap.h"
#include "srslte/common/stack_procedure.h"
namespace srsenb {
@ -49,10 +50,9 @@ class s1ap : public s1ap_interface_rrc, public thread
{
public:
s1ap();
~s1ap();
bool init(s1ap_args_t args_, rrc_interface_s1ap *rrc_, srslte::log *s1ap_log_);
bool init(s1ap_args_t args_, rrc_interface_s1ap* rrc_, srslte::log* s1ap_log_, srslte::timer_handler* timers_);
void stop();
void get_metrics(s1ap_metrics_t &m);
void get_metrics(s1ap_metrics_t& m);
void run_thread();
@ -98,9 +98,6 @@ private:
LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT s1setupresponse;
std::map<uint16_t, ue_ctxt_t> ue_ctxt_map;
std::map<uint32_t, uint16_t> enbid_to_rnti_map;
void build_tai_cgi();
bool connect_mme();
bool setup_s1();
@ -140,12 +137,64 @@ private:
uint32_t target_eci,
srslte::plmn_id_t target_plmn,
srslte::unique_byte_buffer_t rrc_container);
bool handle_hopreparationfailure(LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT* msg);
bool find_mme_ue_id(uint32_t mme_ue_id, uint16_t* rnti, uint32_t* enb_ue_id);
std::string get_cause(LIBLTE_S1AP_CAUSE_STRUCT* c);
std::string get_cause(const LIBLTE_S1AP_CAUSE_STRUCT* c);
// UE-specific data and procedures
struct ue {
class ho_prep_proc_t
{
public:
struct ts1_reloc_prep_expired {
};
ho_prep_proc_t(s1ap::ue* ue_);
srslte::proc_outcome_t
init(uint32_t target_eci_, srslte::plmn_id_t target_plmn_, srslte::unique_byte_buffer_t rrc_container);
srslte::proc_outcome_t step() { return srslte::proc_outcome_t::yield; }
srslte::proc_outcome_t react(ts1_reloc_prep_expired e);
srslte::proc_outcome_t react(const LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT& msg);
const char* name() { return "HandoverPreparation"; }
private:
s1ap::ue* ue_ptr = nullptr;
s1ap* s1ap_ptr = nullptr;
uint32_t target_eci = 0;
srslte::plmn_id_t target_plmn;
};
explicit ue(uint16_t rnti, s1ap* s1ap_ptr_);
bool start_ho_preparation(uint32_t target_eci_,
srslte::plmn_id_t target_plmn_,
srslte::unique_byte_buffer_t rrc_container);
ue_ctxt_t& get_ctxt() { return ctxt; }
srslte::proc_t<ho_prep_proc_t>& get_ho_prep_proc() { return ho_prep_proc; }
private:
bool
send_ho_required(uint32_t target_eci_, srslte::plmn_id_t target_plmn_, srslte::unique_byte_buffer_t rrc_container);
s1ap* s1ap_ptr;
srslte::log* s1ap_log;
ue_ctxt_t ctxt = {};
srslte::timer_handler::unique_timer ts1_reloc_prep; ///< TS1_{RELOCprep} - max time for HO preparation
// user procedures
srslte::proc_t<ho_prep_proc_t> ho_prep_proc;
};
std::map<uint16_t, std::unique_ptr<ue> > users;
std::map<uint32_t, uint16_t> enbid_to_rnti_map;
ue_ctxt_t* get_user_ctxt(uint16_t rnti);
// timers
srslte::timer_handler* timers = nullptr;
};
} // namespace srsenb
#endif // SRSENB_S1AP_H

@ -60,6 +60,7 @@ cell_list =
// root_seq_idx = 204;
// dl_earfcn = 3400;
// ul_earfcn = 474;
ho_active = false;
// CA cells
scell_list = (
@ -69,11 +70,11 @@ cell_list =
// Cells available for handover
meas_cell_list =
(
//{
// cell_idx = 0x19C02;
// dl_earfcn = 2850;
// pci = 2;
//}
{
eci = 0x19C02;
dl_earfcn = 2850;
pci = 2;
}
);
// ReportCfg (only A3 supported)

@ -1195,6 +1195,7 @@ static int parse_meas_report_desc(rrc_meas_cfg_t* meas_cfg, Setting& root)
meas_item.max_report_cells = 1; // TODO: parse
meas_item.report_amount.value = report_cfg_eutra_s::report_amount_e_::r1; // TODO: parse
meas_item.report_interv.value = report_interv_e::ms120; // TODO: parse
meas_item.report_quant.value = report_cfg_eutra_s::report_quant_opts::both; // TODO: parse
// quant coeff parsing
auto& quant = meas_cfg->quant_cfg;
HANDLEPARSERCODE(asn1_parsers::number_to_enum(event.time_to_trigger, root["a3_time_to_trigger"]));
@ -1225,8 +1226,10 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root)
parse_default_field(cell_cfg.dl_earfcn, cellroot, "dl_earfcn", args->enb.dl_earfcn);
parse_default_field(cell_cfg.ul_earfcn, cellroot, "ul_earfcn", args->enb.ul_earfcn);
if (root["ho_active"]) {
HANDLEPARSERCODE(parse_meas_cell_list(&rrc_cfg->meas_cfg, cellroot["meas_cell_list"]));
HANDLEPARSERCODE(parse_meas_report_desc(&rrc_cfg->meas_cfg, cellroot["meas_report_desc"]));
}
cell_cfg.scell_list.resize(cellroot["scell_list"].getLength());
for (uint32_t i = 0; i < cell_cfg.scell_list.size(); ++i) {

@ -121,7 +121,7 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_)
rlc.init(&pdcp, &rrc, &mac, &timers, &rlc_log);
pdcp.init(&rlc, &rrc, &gtpu);
rrc.init(&rrc_cfg, phy, &mac, &rlc, &pdcp, &s1ap, &gtpu, &rrc_log);
s1ap.init(args.s1ap, &rrc, &s1ap_log);
s1ap.init(args.s1ap, &rrc, &s1ap_log, &timers);
gtpu.init(args.s1ap.gtp_bind_addr,
args.s1ap.mme_addr,
args.embms.m1u_multiaddr,

@ -216,7 +216,7 @@ void rrc::add_user(uint16_t rnti)
pthread_mutex_lock(&user_mutex);
auto user_it = users.find(rnti);
if (user_it == users.end()) {
users.insert(std::make_pair(rnti, ue{this, rnti}));
users[rnti].reset(new ue{this, rnti});
rlc->add_user(rnti);
pdcp->add_user(rnti);
rrc_log->info("Added new user rnti=0x%x\n", rnti);
@ -257,10 +257,10 @@ void rrc::upd_user(uint16_t new_rnti, uint16_t old_rnti)
pthread_mutex_lock(&user_mutex);
auto old_it = users.find(old_rnti);
if (old_it != users.end()) {
if (old_it->second.is_connected()) {
old_it->second.send_connection_reconf_upd(srslte::allocate_unique_buffer(*pool));
if (old_it->second->is_connected()) {
old_it->second->send_connection_reconf_upd(srslte::allocate_unique_buffer(*pool));
} else {
old_it->second.send_connection_release();
old_it->second->send_connection_release();
}
}
pthread_mutex_unlock(&user_mutex);
@ -300,7 +300,7 @@ void rrc::write_dl_info(uint16_t rnti, srslte::unique_byte_buffer_t sdu)
sdu->clear();
user_it->second.send_dl_dcch(&dl_dcch_msg, std::move(sdu));
user_it->second->send_dl_dcch(&dl_dcch_msg, std::move(sdu));
} else {
rrc_log->error("Rx SDU for unknown rnti=0x%x\n", rnti);
}
@ -365,29 +365,29 @@ bool rrc::setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRE
}
// UEAggregateMaximumBitrate
user_it->second.set_bitrates(&msg->uEaggregateMaximumBitrate);
user_it->second->set_bitrates(&msg->uEaggregateMaximumBitrate);
// UESecurityCapabilities
user_it->second.set_security_capabilities(&msg->UESecurityCapabilities);
user_it->second->set_security_capabilities(&msg->UESecurityCapabilities);
// SecurityKey
uint8_t key[32];
liblte_pack(msg->SecurityKey.buffer, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN, key);
user_it->second.set_security_key(key, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN / 8);
user_it->second->set_security_key(key, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN / 8);
// CSFB
if (msg->CSFallbackIndicator_present) {
if (msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_REQUIRED ||
msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_HIGH_PRIORITY) {
user_it->second.is_csfb = true;
user_it->second->is_csfb = true;
}
}
// Send RRC security mode command
user_it->second.send_security_mode_command();
user_it->second->send_security_mode_command();
// Setup E-RABs
user_it->second.setup_erabs(&msg->E_RABToBeSetupListCtxtSUReq);
user_it->second->setup_erabs(&msg->E_RABToBeSetupListCtxtSUReq);
pthread_mutex_unlock(&user_mutex);
@ -412,7 +412,7 @@ bool rrc::modify_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIO
if (msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_REQUIRED ||
msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_HIGH_PRIORITY) {
/* Remember that we are in a CSFB right now */
user_it->second.is_csfb = true;
user_it->second->is_csfb = true;
}
}
@ -441,22 +441,22 @@ bool rrc::modify_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIO
// UEAggregateMaximumBitrate
if (msg->uEaggregateMaximumBitrate_present) {
user_it->second.set_bitrates(&msg->uEaggregateMaximumBitrate);
user_it->second->set_bitrates(&msg->uEaggregateMaximumBitrate);
}
// UESecurityCapabilities
if (msg->UESecurityCapabilities_present) {
user_it->second.set_security_capabilities(&msg->UESecurityCapabilities);
user_it->second->set_security_capabilities(&msg->UESecurityCapabilities);
}
// SecurityKey
if (msg->SecurityKey_present) {
uint8_t key[32];
liblte_pack(msg->SecurityKey.buffer, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN, key);
user_it->second.set_security_key(key, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN / 8);
user_it->second->set_security_key(key, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN / 8);
// Send RRC security mode command ??
user_it->second.send_security_mode_command();
user_it->second->send_security_mode_command();
}
pthread_mutex_unlock(&user_mutex);
@ -479,11 +479,11 @@ bool rrc::setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_ST
if (msg->uEaggregateMaximumBitrate_present) {
// UEAggregateMaximumBitrate
user_it->second.set_bitrates(&msg->uEaggregateMaximumBitrate);
user_it->second->set_bitrates(&msg->uEaggregateMaximumBitrate);
}
// Setup E-RABs
user_it->second.setup_erabs(&msg->E_RABToBeSetupListBearerSUReq);
user_it->second->setup_erabs(&msg->E_RABToBeSetupListBearerSUReq);
pthread_mutex_unlock(&user_mutex);
@ -502,7 +502,7 @@ bool rrc::release_erabs(uint32_t rnti)
return false;
}
bool ret = user_it->second.release_erabs();
bool ret = user_it->second->release_erabs();
pthread_mutex_unlock(&user_mutex);
return ret;
}
@ -649,7 +649,7 @@ void rrc::parse_ul_ccch(uint16_t rnti, srslte::unique_byte_buffer_t pdu)
switch (ul_ccch_msg.msg.c1().type()) {
case ul_ccch_msg_type_c::c1_c_::types::rrc_conn_request:
if (user_it != users.end()) {
user_it->second.handle_rrc_con_req(&ul_ccch_msg.msg.c1().rrc_conn_request());
user_it->second->handle_rrc_con_req(&ul_ccch_msg.msg.c1().rrc_conn_request());
} else {
rrc_log->error("Received ConnectionSetup for rnti=0x%x without context\n", rnti);
}
@ -670,7 +670,7 @@ void rrc::parse_ul_ccch(uint16_t rnti, srslte::unique_byte_buffer_t pdu)
.crit_exts.rrc_conn_reest_request_r8()
.reest_cause.to_string()
.c_str());
if (user_it->second.is_idle()) {
if (user_it->second->is_idle()) {
old_rnti = (uint16_t)ul_ccch_msg.msg.c1()
.rrc_conn_reest_request()
.crit_exts.rrc_conn_reest_request_r8()
@ -678,11 +678,11 @@ void rrc::parse_ul_ccch(uint16_t rnti, srslte::unique_byte_buffer_t pdu)
if (users.count(old_rnti)) {
rrc_log->error("Not supported: ConnectionReestablishment for rnti=0x%x. Sending Connection Reject\n",
old_rnti);
user_it->second.send_connection_reest_rej();
user_it->second->send_connection_reest_rej();
s1ap->user_release(old_rnti, LIBLTE_S1AP_CAUSERADIONETWORK_RELEASE_DUE_TO_EUTRAN_GENERATED_REASON);
} else {
rrc_log->error("Received ConnectionReestablishment for rnti=0x%x without context\n", old_rnti);
user_it->second.send_connection_reest_rej();
user_it->second->send_connection_reest_rej();
}
// remove temporal rnti
rrc_log->warning(
@ -704,7 +704,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, srslte::unique_byte_buffer
if (pdu) {
auto user_it = users.find(rnti);
if (user_it != users.end()) {
user_it->second.parse_ul_dcch(lcid, std::move(pdu));
user_it->second->parse_ul_dcch(lcid, std::move(pdu));
} else {
rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti);
}
@ -715,7 +715,7 @@ void rrc::process_rl_failure(uint16_t rnti)
{
auto user_it = users.find(rnti);
if (user_it != users.end()) {
uint32_t n_rfl = user_it->second.rl_failure();
uint32_t n_rfl = user_it->second->rl_failure();
if (n_rfl == 1) {
rrc_log->info("Radio-Link failure detected rnti=0x%x\n", rnti);
if (s1ap->user_exists(rnti)) {
@ -740,9 +740,9 @@ void rrc::process_release_complete(uint16_t rnti)
rrc_log->info("Received Release Complete rnti=0x%x\n", rnti);
auto user_it = users.find(rnti);
if (user_it != users.end()) {
if (!user_it->second.is_idle()) {
if (!user_it->second->is_idle()) {
rlc->clear_buffer(rnti);
user_it->second.send_connection_release();
user_it->second->send_connection_release();
// There is no RRCReleaseComplete message from UE thus wait ~50 subframes for tx
usleep(50000);
}
@ -770,8 +770,8 @@ void rrc::rem_user(uint16_t rnti)
pdcp->rem_user(rnti);
// And deallocate resources from RRC
user_it->second.sr_free();
user_it->second.cqi_free();
user_it->second->sr_free();
user_it->second->cqi_free();
users.erase(rnti);
rrc_log->info("Removed user rnti=0x%x\n", rnti);
@ -971,7 +971,7 @@ void rrc::run_thread()
process_rl_failure(p.rnti);
break;
case LCID_ACT_USER:
user_it->second.set_activity();
user_it->second->set_activity();
break;
case LCID_EXIT:
rrc_log->info("Exiting thread\n");
@ -1028,14 +1028,15 @@ void rrc::activity_monitor::run_thread()
}
}
}
if (rem_rnti) {
if (rem_rnti > 0) {
if (parent->s1ap->user_exists(rem_rnti)) {
parent->s1ap->user_release(rem_rnti, LIBLTE_S1AP_CAUSERADIONETWORK_USER_INACTIVITY);
} else {
if (rem_rnti != SRSLTE_MRNTI)
if (rem_rnti != SRSLTE_MRNTI) {
parent->rem_user_thread(rem_rnti);
}
}
}
pthread_mutex_unlock(&parent->user_mutex);
}
}

@ -776,13 +776,13 @@ bool rrc::ue::rrc_mobility::send_s1_ho_required(uint32_t target_eci, uint8_t mea
capitem.feature_group_inds_present = true;
capitem.feature_group_inds.from_number(0xe6041000); // 0x5d0ffc80); // 0xe6041c00;
{
hoprep_r8.ue_radio_access_cap_info[0].ue_cap_rat_container.resize(128);
asn1::bit_ref bref(&hoprep_r8.ue_radio_access_cap_info[0].ue_cap_rat_container[0],
hoprep_r8.ue_radio_access_cap_info[0].ue_cap_rat_container.size());
uint8_t buffer[128];
asn1::bit_ref bref(&buffer[0], sizeof(buffer));
if (capitem.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) {
rrc_log->error("Failed to pack UE EUTRA Capability\n");
}
hoprep_r8.ue_radio_access_cap_info[0].ue_cap_rat_container.resize((uint32_t)bref.distance_bytes());
memcpy(&hoprep_r8.ue_radio_access_cap_info[0].ue_cap_rat_container[0], &buffer[0], bref.distance_bytes());
}
Debug("UE RA Category: %d\n", capitem.ue_category);
} else {

@ -36,7 +36,49 @@
using srslte::s1ap_mccmnc_to_plmn;
using srslte::uint32_to_uint8;
namespace srsenb{
#define procError(fmt, ...) s1ap_ptr->s1ap_log->error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define procInfo(fmt, ...) s1ap_ptr->s1ap_log->info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
namespace srsenb {
/*********************************************************
* TS 36.413 - Section 8.4.1 - "Handover Preparation"
*********************************************************/
s1ap::ue::ho_prep_proc_t::ho_prep_proc_t(s1ap::ue* ue_) : ue_ptr(ue_), s1ap_ptr(ue_->s1ap_ptr) {}
srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::init(uint32_t target_eci_,
srslte::plmn_id_t target_plmn_,
srslte::unique_byte_buffer_t rrc_container)
{
target_eci = target_eci_;
target_plmn = target_plmn_;
procInfo("Sending HandoverRequired to MME id=%d\n", ue_ptr->ctxt.MME_UE_S1AP_ID);
if (not ue_ptr->send_ho_required(target_eci, target_plmn, std::move(rrc_container))) {
procError("Failed to send HORequired to cell 0x%x\n", target_eci);
return srslte::proc_outcome_t::error;
}
ue_ptr->ts1_reloc_prep.run();
return srslte::proc_outcome_t::yield;
}
srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(ts1_reloc_prep_expired e)
{
// do nothing for now
procError("timer TS1Relocprep has expired.\n");
return srslte::proc_outcome_t::error;
}
srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(const LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT& msg)
{
std::string cause = s1ap_ptr->get_cause(&msg.Cause);
procError("HO preparation Failure. Cause: %s\n", cause.c_str());
s1ap_ptr->s1ap_log->console("HO preparation Failure. Cause: %s\n", cause.c_str());
return srslte::proc_outcome_t::error;
}
/*********************************************************
* S1AP class
*********************************************************/
s1ap::s1ap() :
thread("S1AP"),
@ -46,14 +88,16 @@ s1ap::s1ap() :
mme_connected(false),
running(false),
next_eNB_UE_S1AP_ID(1),
next_ue_stream_id(1){};
s1ap::~s1ap(){};
next_ue_stream_id(1)
{
}
bool s1ap::init(s1ap_args_t args_, rrc_interface_s1ap *rrc_, srslte::log *s1ap_log_)
bool s1ap::init(s1ap_args_t args_, rrc_interface_s1ap* rrc_, srslte::log* s1ap_log_, srslte::timer_handler* timers_)
{
rrc = rrc_;
args = args_;
s1ap_log = s1ap_log_;
timers = timers_;
pool = srslte::byte_buffer_pool::get_instance();
mme_connected = false;
@ -89,10 +133,9 @@ void s1ap::get_metrics(s1ap_metrics_t &m)
}
if(mme_connected) {
m.status = S1AP_READY;
}else{
} else {
m.status = S1AP_ATTACHING;
}
return;
}
void s1ap::run_thread()
@ -188,11 +231,7 @@ void s1ap::build_tai_cgi()
********************************************************************************/
void s1ap::initial_ue(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, srslte::unique_byte_buffer_t pdu)
{
ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++;
ue_ctxt_map[rnti].stream_id = 1;
ue_ctxt_map[rnti].release_requested = false;
gettimeofday(&ue_ctxt_map[rnti].init_timestamp, nullptr);
enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti;
users.insert(std::make_pair(rnti, std::unique_ptr<ue>(new ue{rnti, this})));
send_initialuemessage(rnti, cause, std::move(pdu), false);
}
@ -202,10 +241,7 @@ void s1ap::initial_ue(uint16_t rnti,
uint32_t m_tmsi,
uint8_t mmec)
{
ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++;
ue_ctxt_map[rnti].stream_id = 1;
ue_ctxt_map[rnti].release_requested = false;
enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti;
users.insert(std::make_pair(rnti, std::unique_ptr<ue>(new ue{rnti, this})));
send_initialuemessage(rnti, cause, std::move(pdu), true, m_tmsi, mmec);
}
@ -213,26 +249,23 @@ void s1ap::write_pdu(uint16_t rnti, srslte::unique_byte_buffer_t pdu)
{
s1ap_log->info_hex(pdu->msg, pdu->N_bytes, "Received RRC SDU");
if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) {
s1ap_log->warning("User RNTI:0x%x context not found\n", rnti);
return;
}
if (get_user_ctxt(rnti) != nullptr) {
send_ulnastransport(rnti, std::move(pdu));
}
}
bool s1ap::user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio)
{
s1ap_log->info("User inactivity - RNTI:0x%x\n", rnti);
if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) {
s1ap_log->warning("User RNTI:0x%x context not found\n", rnti);
ue_ctxt_t* ctxt = get_user_ctxt(rnti);
if (ctxt == nullptr) {
return false;
}
if(ue_ctxt_map[rnti].release_requested) {
if (ctxt->release_requested) {
s1ap_log->warning("UE context for RNTI:0x%x is in zombie state. Releasing...\n", rnti);
ue_ctxt_map.erase(rnti);
users.erase(rnti);
rrc->release_complete(rnti);
return false;
}
@ -243,16 +276,16 @@ bool s1ap::user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_
cause.choice.radioNetwork.ext = false;
cause.choice.radioNetwork.e = cause_radio;
ue_ctxt_map[rnti].release_requested = true;
ctxt->release_requested = true;
return send_uectxtreleaserequest(rnti, &cause);
}
bool s1ap::user_exists(uint16_t rnti)
{
return ue_ctxt_map.end() != ue_ctxt_map.find(rnti);
return users.end() != users.find(rnti);
}
void s1ap::ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res)
void s1ap::ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT* res)
{
if(res->E_RABSetupListCtxtSURes.len > 0) {
send_initial_ctxt_setup_response(rnti, res);
@ -457,11 +490,14 @@ bool s1ap::handle_successfuloutcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg)
bool s1ap::handle_unsuccessfuloutcome(LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg)
{
switch(msg->choice_type) {
switch (msg->choice_type) {
case LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_S1SETUPFAILURE:
return handle_s1setupfailure(&msg->choice.S1SetupFailure);
case LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_HANDOVERPREPARATIONFAILURE:
return handle_hopreparationfailure(&msg->choice.HandoverPreparationFailure);
default:
s1ap_log->error("Unhandled unsuccessful outcome message: %s\n", liblte_s1ap_unsuccessfuloutcome_choice_text[msg->choice_type]);
s1ap_log->error("Unhandled unsuccessful outcome message: %s\n",
liblte_s1ap_unsuccessfuloutcome_choice_text[msg->choice_type]);
}
return true;
}
@ -485,9 +521,10 @@ bool s1ap::handle_dlnastransport(LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT
return false;
}
uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID];
ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
ue_ctxt_t* ctxt = get_user_ctxt(rnti);
ctxt->MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
if(msg->HandoverRestrictionList_present) {
if (msg->HandoverRestrictionList_present) {
s1ap_log->warning("Not handling HandoverRestrictionList\n");
}
if(msg->SubscriberProfileIDforRFP_present) {
@ -495,18 +532,17 @@ bool s1ap::handle_dlnastransport(LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT
}
srslte::unique_byte_buffer_t pdu = srslte::allocate_unique_buffer(*pool);
if (pdu) {
if (pdu == nullptr) {
s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::run_thread().\n");
return false;
}
memcpy(pdu->msg, msg->NAS_PDU.buffer, msg->NAS_PDU.n_octets);
pdu->N_bytes = msg->NAS_PDU.n_octets;
rrc->write_dl_info(rnti, std::move(pdu));
return true;
} else {
s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::run_thread().\n");
return false;
}
}
bool s1ap::handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg)
bool s1ap::handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT* msg)
{
s1ap_log->info("Received InitialContextSetupRequest\n");
if(msg->ext) {
@ -517,11 +553,11 @@ bool s1ap::handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETU
return false;
}
uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID];
if(msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ue_ctxt_map[rnti].MME_UE_S1AP_ID) {
s1ap_log->warning("MME_UE_S1AP_ID has changed - old:%d, new:%d\n",
ue_ctxt_map[rnti].MME_UE_S1AP_ID,
msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID);
ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
ue_ctxt_t* ctxt = get_user_ctxt(rnti);
if (msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ctxt->MME_UE_S1AP_ID) {
s1ap_log->warning(
"MME_UE_S1AP_ID has changed - old:%d, new:%d\n", ctxt->MME_UE_S1AP_ID, msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID);
ctxt->MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
}
// Setup UE ctxt in RRC
@ -541,7 +577,7 @@ bool s1ap::handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETU
cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_CS_FALLBACK_TRIGGERED;
/* FIXME: This should normally probably only be sent after the SecurityMode procedure has completed! */
ue_ctxt_map[rnti].release_requested = true;
ctxt->release_requested = true;
send_uectxtreleaserequest(rnti, &cause);
}
}
@ -573,19 +609,15 @@ bool s1ap::handle_erabsetuprequest(LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT
return false;
}
uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID];
if(msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ue_ctxt_map[rnti].MME_UE_S1AP_ID) {
s1ap_log->warning("MME_UE_S1AP_ID has changed - old:%d, new:%d\n",
ue_ctxt_map[rnti].MME_UE_S1AP_ID,
msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID);
ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
ue_ctxt_t* ctxt = get_user_ctxt(rnti);
if (msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ctxt->MME_UE_S1AP_ID) {
s1ap_log->warning(
"MME_UE_S1AP_ID has changed - old:%d, new:%d\n", ctxt->MME_UE_S1AP_ID, msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID);
ctxt->MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
}
// Setup UE ctxt in RRC
if(!rrc->setup_ue_erabs(rnti, msg)) {
return false;
}
return true;
return rrc->setup_ue_erabs(rnti, msg);
}
bool s1ap::handle_uecontextmodifyrequest(LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT* msg)
@ -596,11 +628,11 @@ bool s1ap::handle_uecontextmodifyrequest(LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATI
return false;
}
uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID];
if (msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ue_ctxt_map[rnti].MME_UE_S1AP_ID) {
s1ap_log->warning("MME_UE_S1AP_ID has changed - old:%d, new:%d\n",
ue_ctxt_map[rnti].MME_UE_S1AP_ID,
msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID);
ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
ue_ctxt_t* ctxt = get_user_ctxt(rnti);
if (msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ctxt->MME_UE_S1AP_ID) {
s1ap_log->warning(
"MME_UE_S1AP_ID has changed - old:%d, new:%d\n", ctxt->MME_UE_S1AP_ID, msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID);
ctxt->MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
}
if (!rrc->modify_ue_ctxt(rnti, msg)) {
@ -627,7 +659,7 @@ bool s1ap::handle_uecontextmodifyrequest(LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATI
cause.choice.radioNetwork.ext = false;
cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_CS_FALLBACK_TRIGGERED;
ue_ctxt_map[rnti].release_requested = true;
ctxt->release_requested = true;
send_uectxtreleaserequest(rnti, &cause);
}
}
@ -673,14 +705,14 @@ bool s1ap::handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMA
enbid_to_rnti_map.erase(enb_ue_id);
}
if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) {
s1ap_log->warning("UE context for RNTI:0x%x not found - discarding message\n", rnti);
ue_ctxt_t* ctxt = get_user_ctxt(rnti);
if (ctxt == nullptr) {
return false;
}
rrc->release_erabs(rnti);
send_uectxtreleasecomplete(rnti, ue_ctxt_map[rnti].MME_UE_S1AP_ID, ue_ctxt_map[rnti].eNB_UE_S1AP_ID);
ue_ctxt_map.erase(rnti);
send_uectxtreleasecomplete(rnti, ctxt->MME_UE_S1AP_ID, ctxt->eNB_UE_S1AP_ID);
users.erase(rnti);
s1ap_log->info("UE context for RNTI:0x%x released\n", rnti);
rrc->release_complete(rnti);
return true;
@ -693,6 +725,16 @@ bool s1ap::handle_s1setupfailure(LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *msg)
return true;
}
bool s1ap::handle_hopreparationfailure(LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT* msg)
{
auto user_it = users.find(enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID]);
if (user_it == users.end()) {
s1ap_log->error("user rnti=0x%x no longer exists\n", user_it->first);
}
user_it->second->get_ho_prep_proc().trigger(*msg);
return true;
}
/*******************************************************************************
/* S1AP message senders
********************************************************************************/
@ -741,7 +783,7 @@ bool s1ap::send_initialuemessage(uint16_t rnti,
}
// ENB_UE_S1AP_ID
initue->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
initue->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = get_user_ctxt(rnti)->eNB_UE_S1AP_ID;
// NAS_PDU
memcpy(initue->NAS_PDU.buffer, pdu->msg, pdu->N_bytes);
@ -760,10 +802,17 @@ bool s1ap::send_initialuemessage(uint16_t rnti,
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending InitialUEMessage for RNTI:0x%x", rnti);
ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
if(n_sent == -1) {
ssize_t n_sent = sctp_sendmsg(socket_fd,
msg.msg,
msg.N_bytes,
(struct sockaddr*)&mme_addr,
sizeof(struct sockaddr_in),
htonl(PPID),
0,
get_user_ctxt(rnti)->stream_id,
0,
0);
if (n_sent == -1) {
s1ap_log->error("Failed to send InitialUEMessage for RNTI:0x%x\n", rnti);
return false;
}
@ -793,9 +842,9 @@ bool s1ap::send_ulnastransport(uint16_t rnti, srslte::unique_byte_buffer_t pdu)
ultx->SIPTO_L_GW_TransportLayerAddress_present = false;
// MME_UE_S1AP_ID
ultx->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
ultx->MME_UE_S1AP_ID.MME_UE_S1AP_ID = get_user_ctxt(rnti)->MME_UE_S1AP_ID;
// ENB_UE_S1AP_ID
ultx->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
ultx->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = get_user_ctxt(rnti)->eNB_UE_S1AP_ID;
// NAS_PDU
memcpy(ultx->NAS_PDU.buffer, pdu->msg, pdu->N_bytes);
@ -810,10 +859,17 @@ bool s1ap::send_ulnastransport(uint16_t rnti, srslte::unique_byte_buffer_t pdu)
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UplinkNASTransport for RNTI:0x%x", rnti);
ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
if(n_sent == -1) {
ssize_t n_sent = sctp_sendmsg(socket_fd,
msg.msg,
msg.N_bytes,
(struct sockaddr*)&mme_addr,
sizeof(struct sockaddr_in),
htonl(PPID),
0,
get_user_ctxt(rnti)->stream_id,
0,
0);
if (n_sent == -1) {
s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti);
return false;
}
@ -841,9 +897,9 @@ bool s1ap::send_uectxtreleaserequest(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT *ca
req->GWContextReleaseIndication_present = false;
// MME_UE_S1AP_ID
req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = get_user_ctxt(rnti)->MME_UE_S1AP_ID;
// ENB_UE_S1AP_ID
req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = get_user_ctxt(rnti)->eNB_UE_S1AP_ID;
// Cause
memcpy(&req->Cause, cause, sizeof(LIBLTE_S1AP_CAUSE_STRUCT));
@ -851,10 +907,17 @@ bool s1ap::send_uectxtreleaserequest(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT *ca
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UEContextReleaseRequest for RNTI:0x%x", rnti);
ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
if(n_sent == -1) {
ssize_t n_sent = sctp_sendmsg(socket_fd,
msg.msg,
msg.N_bytes,
(struct sockaddr*)&mme_addr,
sizeof(struct sockaddr_in),
htonl(PPID),
0,
get_user_ctxt(rnti)->stream_id,
0,
0);
if (n_sent == -1) {
s1ap_log->error("Failed to send UEContextReleaseRequest for RNTI:0x%x\n", rnti);
return false;
}
@ -888,10 +951,17 @@ bool s1ap::send_uectxtreleasecomplete(uint16_t rnti, uint32_t mme_ue_id, uint32_
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UEContextReleaseComplete for RNTI:0x%x", rnti);
ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
if(n_sent == -1) {
ssize_t n_sent = sctp_sendmsg(socket_fd,
msg.msg,
msg.N_bytes,
(struct sockaddr*)&mme_addr,
sizeof(struct sockaddr_in),
htonl(PPID),
0,
get_user_ctxt(rnti)->stream_id,
0,
0);
if (n_sent == -1) {
s1ap_log->error("Failed to send UEContextReleaseComplete for RNTI:0x%x\n", rnti);
return false;
}
@ -933,17 +1003,24 @@ bool s1ap::send_initial_ctxt_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_I
}
// Fill in the MME and eNB IDs
res->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
res->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
res->MME_UE_S1AP_ID.MME_UE_S1AP_ID = get_user_ctxt(rnti)->MME_UE_S1AP_ID;
res->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = get_user_ctxt(rnti)->eNB_UE_S1AP_ID;
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get());
s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending InitialContextSetupResponse for RNTI:0x%x", rnti);
ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes,
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
ssize_t n_sent = sctp_sendmsg(socket_fd,
buf->msg,
buf->N_bytes,
(struct sockaddr*)&mme_addr,
sizeof(struct sockaddr_in),
htonl(PPID),
0,
get_user_ctxt(rnti)->stream_id,
0,
0);
if(n_sent == -1) {
if (n_sent == -1) {
s1ap_log->error("Failed to send InitialContextSetupResponse for RNTI:0x%x\n", rnti);
return false;
}
@ -985,17 +1062,24 @@ bool s1ap::send_erab_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETU
}
// Fill in the MME and eNB IDs
res->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
res->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
res->MME_UE_S1AP_ID.MME_UE_S1AP_ID = get_user_ctxt(rnti)->MME_UE_S1AP_ID;
res->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = get_user_ctxt(rnti)->eNB_UE_S1AP_ID;
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get());
s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending E_RABSetupResponse for RNTI:0x%x", rnti);
ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes,
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
ssize_t n_sent = sctp_sendmsg(socket_fd,
buf->msg,
buf->N_bytes,
(struct sockaddr*)&mme_addr,
sizeof(struct sockaddr_in),
htonl(PPID),
0,
get_user_ctxt(rnti)->stream_id,
0,
0);
if(n_sent == -1) {
if (n_sent == -1) {
s1ap_log->error("Failed to send E_RABSetupResponse for RNTI:0x%x\n", rnti);
return false;
}
@ -1026,8 +1110,8 @@ bool s1ap::send_initial_ctxt_setup_failure(uint16_t rnti)
fail->ext = false;
fail->CriticalityDiagnostics_present = false;
fail->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
fail->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
fail->MME_UE_S1AP_ID.MME_UE_S1AP_ID = get_user_ctxt(rnti)->MME_UE_S1AP_ID;
fail->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = get_user_ctxt(rnti)->eNB_UE_S1AP_ID;
fail->Cause.ext = false;
fail->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK;
@ -1037,11 +1121,18 @@ bool s1ap::send_initial_ctxt_setup_failure(uint16_t rnti)
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get());
s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending InitialContextSetupFailure for RNTI:0x%x", rnti);
ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes,
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
ssize_t n_sent = sctp_sendmsg(socket_fd,
buf->msg,
buf->N_bytes,
(struct sockaddr*)&mme_addr,
sizeof(struct sockaddr_in),
htonl(PPID),
0,
get_user_ctxt(rnti)->stream_id,
0,
0);
if(n_sent == -1) {
if (n_sent == -1) {
s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti);
return false;
}
@ -1072,8 +1163,8 @@ bool s1ap::send_uectxmodifyresp(uint16_t rnti)
resp->ext = false;
resp->CriticalityDiagnostics_present = false;
resp->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
resp->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
resp->MME_UE_S1AP_ID.MME_UE_S1AP_ID = get_user_ctxt(rnti)->MME_UE_S1AP_ID;
resp->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = get_user_ctxt(rnti)->eNB_UE_S1AP_ID;
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get());
s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending ContextModificationFailure for RNTI:0x%x", rnti);
@ -1085,7 +1176,7 @@ bool s1ap::send_uectxmodifyresp(uint16_t rnti)
sizeof(struct sockaddr_in),
htonl(PPID),
0,
ue_ctxt_map[rnti].stream_id,
get_user_ctxt(rnti)->stream_id,
0,
0);
@ -1120,8 +1211,8 @@ bool s1ap::send_uectxmodifyfailure(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT* caus
fail->ext = false;
fail->CriticalityDiagnostics_present = false;
fail->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
fail->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
fail->MME_UE_S1AP_ID.MME_UE_S1AP_ID = get_user_ctxt(rnti)->MME_UE_S1AP_ID;
fail->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = get_user_ctxt(rnti)->eNB_UE_S1AP_ID;
memcpy(&fail->Cause, cause, sizeof(LIBLTE_S1AP_CAUSE_STRUCT));
@ -1135,7 +1226,7 @@ bool s1ap::send_uectxmodifyfailure(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT* caus
sizeof(struct sockaddr_in),
htonl(PPID),
0,
ue_ctxt_map[rnti].stream_id,
get_user_ctxt(rnti)->stream_id,
0,
0);
@ -1159,111 +1250,11 @@ bool s1ap::send_ho_required(uint16_t rnti,
if (!mme_connected) {
return false;
}
auto ueit = ue_ctxt_map.find(rnti);
if (ueit == ue_ctxt_map.end()) {
s1ap_log->error("rnti=0x%x is not recognized.\n", rnti);
return false;
}
ue_ctxt_t* uectxt = &ueit->second;
/*** Setup S1AP PDU as HandoverRequired ***/
LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
bzero(&tx_pdu, sizeof(tx_pdu));
tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE;
tx_pdu.choice.initiatingMessage.choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERREQUIRED;
tx_pdu.choice.initiatingMessage.criticality = LIBLTE_S1AP_CRITICALITY_IGNORE;
tx_pdu.choice.initiatingMessage.procedureCode = LIBLTE_S1AP_PROC_ID_HANDOVERPREPARATION;
LIBLTE_S1AP_MESSAGE_HANDOVERREQUIRED_STRUCT& horeq = tx_pdu.choice.initiatingMessage.choice.HandoverRequired;
/*** fill HO Required message ***/
horeq.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = uectxt->eNB_UE_S1AP_ID;
horeq.MME_UE_S1AP_ID.MME_UE_S1AP_ID = uectxt->MME_UE_S1AP_ID;
horeq.Direct_Forwarding_Path_Availability_present = false; // NOTE: X2 for fwd path not supported
horeq.HandoverType.e = LIBLTE_S1AP_HANDOVERTYPE_INTRALTE; // NOTE: only intra-LTE HO supported
horeq.Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK;
horeq.Cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_UNSPECIFIED;
// LIBLTE_S1AP_CAUSERADIONETWORK_S1_INTRA_SYSTEM_HANDOVER_TRIGGERED;
/*** set the target eNB ***/
horeq.TargetID.choice_type = LIBLTE_S1AP_TARGETID_CHOICE_TARGETENB_ID;
LIBLTE_S1AP_TARGETENB_ID_STRUCT* targetenb = &horeq.TargetID.choice.targeteNB_ID;
horeq.CSG_Id_present = false; // NOTE: CSG/hybrid target cell not supported
horeq.CellAccessMode_present = false; // only for hybrid cells
// no GERAN/UTRAN/PS
horeq.SRVCCHOIndication_present = false;
horeq.MSClassmark2_present = false;
horeq.MSClassmark3_present = false;
horeq.PS_ServiceNotAvailable_present = false;
// set PLMN of target and TAI
if (horeq.TargetID.choice_type != LIBLTE_S1AP_TARGETID_CHOICE_TARGETENB_ID) {
s1ap_log->error("Non-intraLTE HO not supported.\n");
auto it = users.find(rnti);
if (it == users.end()) {
return false;
}
// NOTE: Only HO without TAU supported.
uint16_t tmp16;
tmp16 = htons(args.tac);
memcpy(targetenb->selected_TAI.tAC.buffer, &tmp16, sizeof(uint16_t));
target_plmn.to_s1ap_plmn_bytes(targetenb->selected_TAI.pLMNidentity.buffer);
// NOTE: Only HO to different Macro eNB is supported.
targetenb->global_ENB_ID.eNB_ID.choice_type = LIBLTE_S1AP_ENB_ID_CHOICE_MACROENB_ID;
target_plmn.to_s1ap_plmn_bytes(targetenb->global_ENB_ID.pLMNidentity.buffer);
uint32_t tmp32 = htonl(target_eci >> 8u);
uint8_t enb_id_bits[sizeof(uint32_t) * 8];
liblte_unpack((uint8_t*)&tmp32, sizeof(uint32_t), enb_id_bits);
memcpy(targetenb->global_ENB_ID.eNB_ID.choice.macroENB_ID.buffer,
&enb_id_bits[32 - LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN],
LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN);
/*** fill the transparent container ***/
horeq.Source_ToTarget_TransparentContainer_Secondary_present = false;
LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT transparent_cntr;
bzero(&transparent_cntr, sizeof(LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT));
transparent_cntr.e_RABInformationList_present = false; // TODO: CHECK
transparent_cntr.subscriberProfileIDforRFP_present = false; // TODO: CHECK
// - set target cell ID
target_plmn.to_s1ap_plmn_bytes(transparent_cntr.targetCell_ID.pLMNidentity.buffer);
tmp32 = htonl(target_eci);
uint8_t eci_bits[32];
liblte_unpack((uint8_t*)&tmp32, sizeof(uint32_t), eci_bits);
memcpy(transparent_cntr.targetCell_ID.cell_ID.buffer,
&eci_bits[32 - LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN],
LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN); // [ENBID|CELLID|0]
// info specific to source cell and history of UE
// - set as last visited cell the source eNB PLMN & Cell ID
transparent_cntr.uE_HistoryInformation.len = 1;
transparent_cntr.uE_HistoryInformation.buffer[0].choice_type = LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_E_UTRAN_CELL;
LIBLTE_S1AP_LASTVISITEDEUTRANCELLINFORMATION_STRUCT* lastvisited =
&transparent_cntr.uE_HistoryInformation.buffer[0].choice.e_UTRAN_Cell;
lastvisited->cellType.cell_Size.e = LIBLTE_S1AP_CELL_SIZE_MEDIUM;
target_plmn.to_s1ap_plmn_bytes(lastvisited->global_Cell_ID.pLMNidentity.buffer);
memcpy(
lastvisited->global_Cell_ID.cell_ID.buffer, eutran_cgi.cell_ID.buffer, LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN);
// - set time spent in current source cell
struct timeval ts[3];
memcpy(&ts[1], &uectxt->init_timestamp, sizeof(struct timeval));
gettimeofday(&ts[2], nullptr);
get_time_interval(ts);
lastvisited->time_UE_StayedInCell.Time_UE_StayedInCell = (uint16_t)(ts[0].tv_usec / 1.0e6 + ts[0].tv_sec);
lastvisited->time_UE_StayedInCell.Time_UE_StayedInCell =
std::min(lastvisited->time_UE_StayedInCell.Time_UE_StayedInCell, (uint16_t)4095);
// - fill RRC container
memcpy(transparent_cntr.rRC_Container.buffer, rrc_container->buffer, rrc_container->N_bytes);
transparent_cntr.rRC_Container.n_octets = rrc_container->N_bytes;
/*** pack Transparent Container into HORequired message ***/
LIBLTE_BYTE_MSG_STRUCT bytemsg;
bytemsg.N_bytes = 0;
LIBLTE_BIT_MSG_STRUCT bitmsg;
uint8_t* msg_ptr = bitmsg.msg;
liblte_s1ap_pack_sourceenb_totargetenb_transparentcontainer(&transparent_cntr, &msg_ptr);
bitmsg.N_bits = msg_ptr - bitmsg.msg;
liblte_pack(&bitmsg, &bytemsg);
memcpy(horeq.Source_ToTarget_TransparentContainer.buffer, bytemsg.msg, bytemsg.N_bytes);
horeq.Source_ToTarget_TransparentContainer.n_octets = bytemsg.N_bytes;
// TODO: Start timer TS1_{RELOCprep}
return sctp_send_s1ap_pdu(&tx_pdu, rnti, "HORequired");
return it->second->start_ho_preparation(target_eci, target_plmn, std::move(rrc_container));
}
// bool s1ap::send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps)
@ -1280,8 +1271,8 @@ bool s1ap::send_ho_required(uint16_t rnti,
// LIBLTE_S1AP_MESSAGE_UECAPABILITYINFOINDICATION_STRUCT *caps = &init->choice.UECapabilityInfoIndication;
// caps->ext = false;
// caps->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
// caps->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
// caps->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti]->MME_UE_S1AP_ID;
// caps->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti]->eNB_UE_S1AP_ID;
// // TODO: caps->UERadioCapability.
// liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
@ -1289,7 +1280,7 @@ bool s1ap::send_ho_required(uint16_t rnti,
// ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
// (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
// htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
// htonl(PPID), 0, ue_ctxt_map[rnti]->stream_id, 0, 0);
// if(n_sent == -1) {
// s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti);
// return false;
@ -1319,7 +1310,7 @@ bool s1ap::sctp_send_s1ap_pdu(LIBLTE_S1AP_S1AP_PDU_STRUCT* tx_pdu, uint32_t rnti
sizeof(struct sockaddr_in),
htonl(PPID),
0,
ue_ctxt_map[rnti].stream_id,
get_user_ctxt(rnti)->stream_id,
0,
0);
if (n_sent == -1) {
@ -1332,17 +1323,17 @@ bool s1ap::sctp_send_s1ap_pdu(LIBLTE_S1AP_S1AP_PDU_STRUCT* tx_pdu, uint32_t rnti
bool s1ap::find_mme_ue_id(uint32_t mme_ue_id, uint16_t* rnti, uint32_t* enb_ue_id)
{
for (auto& it : ue_ctxt_map) {
if (it.second.MME_UE_S1AP_ID == mme_ue_id) {
*rnti = it.second.rnti;
*enb_ue_id = it.second.eNB_UE_S1AP_ID;
for (auto& it : users) {
if (it.second->get_ctxt().MME_UE_S1AP_ID == mme_ue_id) {
*rnti = it.second->get_ctxt().rnti;
*enb_ue_id = it.second->get_ctxt().eNB_UE_S1AP_ID;
return true;
}
}
return false;
}
std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c)
std::string s1ap::get_cause(const LIBLTE_S1AP_CAUSE_STRUCT* c)
{
std::string cause = liblte_s1ap_cause_choice_text[c->choice_type];
cause += " - ";
@ -1369,4 +1360,146 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c)
return cause;
}
ue_ctxt_t* s1ap::get_user_ctxt(uint16_t rnti)
{
auto it = users.find(rnti);
if (it == users.end()) {
s1ap_log->warning("User rnti=0x%x context not found\n", rnti);
return nullptr;
}
return &it->second->get_ctxt();
}
/*******************************************************************************
/* s1ap::ue Class
********************************************************************************/
s1ap::ue::ue(uint16_t rnti_, s1ap* s1ap_ptr_) : s1ap_ptr(s1ap_ptr_), s1ap_log(s1ap_ptr_->s1ap_log), ho_prep_proc(this)
{
ctxt.rnti = rnti_;
ctxt.eNB_UE_S1AP_ID = s1ap_ptr->next_eNB_UE_S1AP_ID++;
ctxt.stream_id = 1;
ctxt.release_requested = false;
gettimeofday(&ctxt.init_timestamp, nullptr);
s1ap_ptr->enbid_to_rnti_map[ctxt.eNB_UE_S1AP_ID] = ctxt.rnti;
// initialize timers
ts1_reloc_prep = s1ap_ptr->timers->get_unique_timer();
ts1_reloc_prep.set(10000, [this](uint32_t tid) { ho_prep_proc.trigger(ho_prep_proc_t::ts1_reloc_prep_expired{}); });
}
bool s1ap::ue::start_ho_preparation(uint32_t target_eci,
srslte::plmn_id_t target_plmn,
srslte::unique_byte_buffer_t rrc_container)
{
if (not ho_prep_proc.launch(target_eci, target_plmn, std::move(rrc_container))) {
s1ap_log->error("Failed to initiate an HandoverPreparation procedure for user rnti=0x%x\n", ctxt.rnti);
return false;
}
return true;
}
bool s1ap::ue::send_ho_required(uint32_t target_eci,
srslte::plmn_id_t target_plmn,
srslte::unique_byte_buffer_t rrc_container)
{
/*** Setup S1AP PDU as HandoverRequired ***/
LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
bzero(&tx_pdu, sizeof(tx_pdu));
tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE;
tx_pdu.choice.initiatingMessage.choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERREQUIRED;
tx_pdu.choice.initiatingMessage.criticality = LIBLTE_S1AP_CRITICALITY_IGNORE;
tx_pdu.choice.initiatingMessage.procedureCode = LIBLTE_S1AP_PROC_ID_HANDOVERPREPARATION;
LIBLTE_S1AP_MESSAGE_HANDOVERREQUIRED_STRUCT& horeq = tx_pdu.choice.initiatingMessage.choice.HandoverRequired;
/*** fill HO Required message ***/
horeq.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ctxt.eNB_UE_S1AP_ID;
horeq.MME_UE_S1AP_ID.MME_UE_S1AP_ID = ctxt.MME_UE_S1AP_ID;
horeq.Direct_Forwarding_Path_Availability_present = false; // NOTE: X2 for fwd path not supported
horeq.HandoverType.e = LIBLTE_S1AP_HANDOVERTYPE_INTRALTE; // NOTE: only intra-LTE HO supported
horeq.Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK;
horeq.Cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_UNSPECIFIED;
// LIBLTE_S1AP_CAUSERADIONETWORK_S1_INTRA_SYSTEM_HANDOVER_TRIGGERED;
/*** set the target eNB ***/
horeq.TargetID.choice_type = LIBLTE_S1AP_TARGETID_CHOICE_TARGETENB_ID;
LIBLTE_S1AP_TARGETENB_ID_STRUCT* targetenb = &horeq.TargetID.choice.targeteNB_ID;
horeq.CSG_Id_present = false; // NOTE: CSG/hybrid target cell not supported
horeq.CellAccessMode_present = false; // only for hybrid cells
// no GERAN/UTRAN/PS
horeq.SRVCCHOIndication_present = false;
horeq.MSClassmark2_present = false;
horeq.MSClassmark3_present = false;
horeq.PS_ServiceNotAvailable_present = false;
// set PLMN of target and TAI
if (horeq.TargetID.choice_type != LIBLTE_S1AP_TARGETID_CHOICE_TARGETENB_ID) {
s1ap_log->error("Non-intraLTE HO not supported.\n");
return false;
}
// NOTE: Only HO without TAU supported.
uint16_t tmp16;
tmp16 = htons(s1ap_ptr->args.tac);
memcpy(targetenb->selected_TAI.tAC.buffer, &tmp16, sizeof(uint16_t));
target_plmn.to_s1ap_plmn_bytes(targetenb->selected_TAI.pLMNidentity.buffer);
// NOTE: Only HO to different Macro eNB is supported.
targetenb->global_ENB_ID.eNB_ID.choice_type = LIBLTE_S1AP_ENB_ID_CHOICE_MACROENB_ID;
target_plmn.to_s1ap_plmn_bytes(targetenb->global_ENB_ID.pLMNidentity.buffer);
uint32_t tmp32 = htonl(target_eci >> 8u);
uint8_t enb_id_bits[sizeof(uint32_t) * 8];
liblte_unpack((uint8_t*)&tmp32, sizeof(uint32_t), enb_id_bits);
memcpy(targetenb->global_ENB_ID.eNB_ID.choice.macroENB_ID.buffer,
&enb_id_bits[32 - LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN],
LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN);
/*** fill the transparent container ***/
horeq.Source_ToTarget_TransparentContainer_Secondary_present = false;
LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT transparent_cntr;
bzero(&transparent_cntr, sizeof(LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT));
transparent_cntr.e_RABInformationList_present = false; // TODO: CHECK
transparent_cntr.subscriberProfileIDforRFP_present = false; // TODO: CHECK
// - set target cell ID
target_plmn.to_s1ap_plmn_bytes(transparent_cntr.targetCell_ID.pLMNidentity.buffer);
tmp32 = htonl(target_eci);
uint8_t eci_bits[32];
liblte_unpack((uint8_t*)&tmp32, sizeof(uint32_t), eci_bits);
memcpy(transparent_cntr.targetCell_ID.cell_ID.buffer,
&eci_bits[32 - LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN],
LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN); // [ENBID|CELLID|0]
// info specific to source cell and history of UE
// - set as last visited cell the source eNB PLMN & Cell ID
transparent_cntr.uE_HistoryInformation.len = 1;
transparent_cntr.uE_HistoryInformation.buffer[0].choice_type = LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_E_UTRAN_CELL;
LIBLTE_S1AP_LASTVISITEDEUTRANCELLINFORMATION_STRUCT* lastvisited =
&transparent_cntr.uE_HistoryInformation.buffer[0].choice.e_UTRAN_Cell;
lastvisited->cellType.cell_Size.e = LIBLTE_S1AP_CELL_SIZE_MEDIUM;
target_plmn.to_s1ap_plmn_bytes(lastvisited->global_Cell_ID.pLMNidentity.buffer);
memcpy(lastvisited->global_Cell_ID.cell_ID.buffer,
s1ap_ptr->eutran_cgi.cell_ID.buffer,
LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN);
// - set time spent in current source cell
struct timeval ts[3];
memcpy(&ts[1], &ctxt.init_timestamp, sizeof(struct timeval));
gettimeofday(&ts[2], nullptr);
get_time_interval(ts);
lastvisited->time_UE_StayedInCell.Time_UE_StayedInCell = (uint16_t)(ts[0].tv_usec / 1.0e6 + ts[0].tv_sec);
lastvisited->time_UE_StayedInCell.Time_UE_StayedInCell =
std::min(lastvisited->time_UE_StayedInCell.Time_UE_StayedInCell, (uint16_t)4095);
// - fill RRC container
memcpy(transparent_cntr.rRC_Container.buffer, rrc_container->msg, rrc_container->N_bytes);
transparent_cntr.rRC_Container.n_octets = rrc_container->N_bytes;
/*** pack Transparent Container into HORequired message ***/
LIBLTE_BYTE_MSG_STRUCT bytemsg;
bytemsg.N_bytes = 0;
LIBLTE_BIT_MSG_STRUCT bitmsg;
uint8_t* msg_ptr = bitmsg.msg;
liblte_s1ap_pack_sourceenb_totargetenb_transparentcontainer(&transparent_cntr, &msg_ptr);
bitmsg.N_bits = msg_ptr - bitmsg.msg;
liblte_pack(&bitmsg, &bytemsg);
memcpy(horeq.Source_ToTarget_TransparentContainer.buffer, bytemsg.msg, bytemsg.N_bytes);
horeq.Source_ToTarget_TransparentContainer.n_octets = bytemsg.N_bytes;
return s1ap_ptr->sctp_send_s1ap_pdu(&tx_pdu, ctxt.rnti, "HORequired");
}
} // namespace srsenb

Loading…
Cancel
Save