diff --git a/lib/src/asn1/liblte_s1ap.cc b/lib/src/asn1/liblte_s1ap.cc index 07ce0336e..b7ee7ff9f 100644 --- a/lib/src/asn1/liblte_s1ap.cc +++ b/lib/src/asn1/liblte_s1ap.cc @@ -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; diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index 8d6044cda..266ed81dc 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -347,7 +347,7 @@ public: }; private: - std::map users; + std::map > users; // NOTE: has to have fixed addr std::map pending_paging; diff --git a/srsenb/hdr/stack/upper/s1ap.h b/srsenb/hdr/stack/upper/s1ap.h index a20784a66..6fc19a199 100644 --- a/srsenb/hdr/stack/upper/s1ap.h +++ b/srsenb/hdr/stack/upper/s1ap.h @@ -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 ue_ctxt_map; - std::map 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& 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; + }; + std::map > users; + std::map 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 diff --git a/srsenb/rr.conf.example b/srsenb/rr.conf.example index ae38b37a5..5cc7cf8ad 100644 --- a/srsenb/rr.conf.example +++ b/srsenb/rr.conf.example @@ -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) diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 273c65702..11a4ccd29 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -1192,9 +1192,10 @@ static int parse_meas_report_desc(rrc_meas_cfg_t* meas_cfg, Setting& root) event.event_id.set_event_a3().report_on_leave = false; event.event_id.event_a3().a3_offset = (int)root["a3_offset"]; event.hysteresis = (int)root["a3_hysteresis"]; - 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.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); - 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"])); + 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) { diff --git a/srsenb/src/stack/enb_stack_lte.cc b/srsenb/src/stack/enb_stack_lte.cc index e8d43404a..0c16b1242 100644 --- a/srsenb/src/stack/enb_stack_lte.cc +++ b/srsenb/src/stack/enb_stack_lte.cc @@ -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, >pu); rrc.init(&rrc_cfg, phy, &mac, &rlc, &pdcp, &s1ap, >pu, &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, diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index 3232f771a..473d41517 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -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,12 +1028,13 @@ 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); diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 0ffade042..fa1431021 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -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 { diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index 0baaeeb79..eb79af24b 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -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_; + 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(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(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)); } - - 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) { - case LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_S1SETUPFAILURE: - return handle_s1setupfailure(&msg->choice.S1SetupFailure); - default: - s1ap_log->error("Unhandled unsuccessful outcome message: %s\n", liblte_s1ap_unsuccessfuloutcome_choice_text[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]); } return true; } @@ -484,10 +520,11 @@ bool s1ap::handle_dlnastransport(LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); 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; + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_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) { - 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 { + 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; } -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) { @@ -516,12 +552,12 @@ bool s1ap::handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETU s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); 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; + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_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); } } @@ -572,20 +608,16 @@ bool s1ap::handle_erabsetuprequest(LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); 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; + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_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) @@ -595,12 +627,12 @@ bool s1ap::handle_uecontextmodifyrequest(LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATI s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); 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; + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_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; } @@ -790,12 +839,12 @@ bool s1ap::send_ulnastransport(uint16_t rnti, srslte::unique_byte_buffer_t pdu) ultx->ext = false; ultx->GW_TransportLayerAddress_present = false; ultx->LHN_ID_present = false; - ultx->SIPTO_L_GW_TransportLayerAddress_present = false; + 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; } @@ -838,12 +894,12 @@ bool s1ap::send_uectxtreleaserequest(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT *ca LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *req = &init->choice.UEContextReleaseRequest; req->ext = false; - req->GWContextReleaseIndication_present = false; + 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; } @@ -1023,25 +1107,32 @@ bool s1ap::send_initial_ctxt_setup_failure(uint16_t rnti) unsucc->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPFAILURE; LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPFAILURE_STRUCT *fail = &unsucc->choice.InitialContextSetupFailure; - fail->ext = false; - fail->CriticalityDiagnostics_present = false; + 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; + fail->Cause.ext = false; + fail->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK; fail->Cause.choice.radioNetwork.ext = false; fail->Cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_UNSPECIFIED; 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); + auto it = users.find(rnti); + if (it == users.end()) { 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"); - 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