From 57cd40ca31c988b1f70a0a826425d2a007aaacc6 Mon Sep 17 00:00:00 2001 From: Francisco Paisana Date: Mon, 4 Nov 2019 09:57:18 +0000 Subject: [PATCH] added an ho required message send from SeNB to MME --- lib/include/srslte/common/bcd_helpers.h | 1 - .../srslte/interfaces/enb_interfaces.h | 14 +- .../srslte/interfaces/rrc_interface_types.h | 31 +- srsenb/hdr/stack/rrc/rrc.h | 8 +- srsenb/hdr/stack/rrc/rrc_mobility.h | 37 ++- srsenb/hdr/stack/upper/s1ap.h | 40 ++- srsenb/src/enb_cfg_parser.cc | 4 +- srsenb/src/stack/rrc/rrc.cc | 26 +- srsenb/src/stack/rrc/rrc_mobility.cc | 308 +++++++++++++++++- srsenb/src/stack/upper/s1ap.cc | 165 +++++++++- srsenb/test/upper/rrc_mobility_test.cc | 18 +- 11 files changed, 580 insertions(+), 72 deletions(-) diff --git a/lib/include/srslte/common/bcd_helpers.h b/lib/include/srslte/common/bcd_helpers.h index d05216aa5..cb907827a 100644 --- a/lib/include/srslte/common/bcd_helpers.h +++ b/lib/include/srslte/common/bcd_helpers.h @@ -284,7 +284,6 @@ inline void s1ap_mccmnc_to_plmn(uint16_t mcc, uint16_t mnc, uint32_t *plmn) *plmn |= nibbles[5]; } - } // namespace srslte #endif // SRSLTE_BCD_HELPERS_H diff --git a/lib/include/srslte/interfaces/enb_interfaces.h b/lib/include/srslte/interfaces/enb_interfaces.h index 837c05eb3..493f463af 100644 --- a/lib/include/srslte/interfaces/enb_interfaces.h +++ b/lib/include/srslte/interfaces/enb_interfaces.h @@ -302,11 +302,15 @@ public: uint32_t m_tmsi, uint8_t mmec) = 0; virtual void write_pdu(uint16_t rnti, srslte::unique_byte_buffer_t pdu) = 0; - virtual bool user_exists(uint16_t rnti) = 0; - virtual bool user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio) = 0; - virtual void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res) = 0; - virtual void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res) = 0; - virtual bool is_mme_connected() = 0; + virtual bool user_exists(uint16_t rnti) = 0; + virtual bool user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio) = 0; + virtual void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT* res) = 0; + virtual void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT* res) = 0; + virtual bool is_mme_connected() = 0; + virtual bool send_ho_required(uint16_t rnti, + uint32_t target_eci, + srslte::plmn_id_t target_plmn, + srslte::unique_byte_buffer_t rrc_container) = 0; }; // Combined interface for PHY to access stack (MAC and RRC) diff --git a/lib/include/srslte/interfaces/rrc_interface_types.h b/lib/include/srslte/interfaces/rrc_interface_types.h index 5ba26c400..cc90c107a 100644 --- a/lib/include/srslte/interfaces/rrc_interface_types.h +++ b/lib/include/srslte/interfaces/rrc_interface_types.h @@ -83,6 +83,27 @@ struct plmn_id_t { } return SRSLTE_SUCCESS; } + std::pair to_number() + { + uint16_t mcc_num, mnc_num; + srslte::bytes_to_mcc(&mcc[0], &mcc_num); + srslte::bytes_to_mnc(&mnc[0], &mnc_num, nof_mnc_digits); + return std::make_pair(mcc_num, mnc_num); + } + uint32_t to_s1ap_plmn() + { + auto mcc_mnc_pair = to_number(); + uint32_t s1ap_plmn; + srslte::s1ap_mccmnc_to_plmn(mcc_mnc_pair.first, mcc_mnc_pair.second, &s1ap_plmn); + return s1ap_plmn; + } + void to_s1ap_plmn_bytes(uint8_t* plmn_bytes) + { + uint32_t s1ap_plmn = to_s1ap_plmn(); + s1ap_plmn = htonl(s1ap_plmn); + uint8_t* plmn_ptr = (uint8_t*)&s1ap_plmn; + memcpy(&plmn_bytes[0], plmn_ptr + 1, 3); + } int from_string(const std::string& plmn_str) { if (plmn_str.size() < 5 or plmn_str.size() > 6) { @@ -250,12 +271,12 @@ inline std::string to_string(const rlc_type_t& type) class rlc_config_t { public: - rlc_type_t type; - rlc_mode_t rlc_mode; - rlc_am_config_t am; - rlc_um_config_t um; + rlc_type_t type; + rlc_mode_t rlc_mode; + rlc_am_config_t am; + rlc_um_config_t um; rlc_um_nr_config_t um_nr; - uint32_t tx_queue_length; + uint32_t tx_queue_length; rlc_config_t() : type(rlc_type_t::lte), diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index 7df17a2fa..8d6044cda 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -71,7 +71,7 @@ typedef struct { struct meas_cell_cfg_t { uint32_t earfcn; uint16_t pci; - uint32_t cell_id; + uint32_t eci; float q_offset; }; @@ -285,10 +285,15 @@ public: bool is_csfb = false; private: + // args srslte::byte_buffer_pool* pool = nullptr; struct timeval t_last_activity; struct timeval t_ue_init; + // cached for ease of context transfer + asn1::rrc::rrc_conn_recfg_r8_ies_s last_rrc_conn_recfg; + asn1::rrc::security_algorithm_cfg_s last_security_mode_cmd; + asn1::rrc::establishment_cause_e establishment_cause; std::unique_ptr mobility_handler; @@ -315,6 +320,7 @@ public: LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT bitrates; LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT security_capabilities; + bool eutra_capabilities_unpacked = false; asn1::rrc::ue_eutra_cap_s eutra_capabilities; typedef struct { diff --git a/srsenb/hdr/stack/rrc/rrc_mobility.h b/srsenb/hdr/stack/rrc/rrc_mobility.h index 387fdf7c0..65a87866a 100644 --- a/srsenb/hdr/stack/rrc/rrc_mobility.h +++ b/srsenb/hdr/stack/rrc/rrc_mobility.h @@ -57,6 +57,10 @@ public: // getters const asn1::rrc::meas_obj_to_add_mod_list_l& meas_objs() const { return var_meas.meas_obj_list; } const asn1::rrc::report_cfg_to_add_mod_list_l& rep_cfgs() const { return var_meas.report_cfg_list; } + const asn1::rrc::meas_id_to_add_mod_list_l& meas_ids() const { return var_meas.meas_id_list; } + asn1::rrc::meas_obj_to_add_mod_list_l& meas_objs() { return var_meas.meas_obj_list; } + asn1::rrc::report_cfg_to_add_mod_list_l& rep_cfgs() { return var_meas.report_cfg_list; } + asn1::rrc::meas_id_to_add_mod_list_l& meas_ids() { return var_meas.meas_id_list; } private: asn1::rrc::var_meas_cfg_s var_meas; @@ -71,6 +75,7 @@ public: std::shared_ptr current_meas_cfg; ///< const to enable ptr comparison as identity comparison private: + // args const rrc_cfg_t* cfg = nullptr; }; @@ -79,8 +84,13 @@ class rrc::ue::rrc_mobility public: explicit rrc_mobility(srsenb::rrc::ue* outer_ue); bool fill_conn_recfg_msg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg); + void handle_ue_meas_report(const asn1::rrc::meas_report_s& msg); private: + enum class ho_interface_t { S1, X2, interSector }; + + bool send_s1_ho_required(uint32_t target_eci, uint8_t measobj_id, bool fwd_direct_path_available); + rrc::ue* rrc_ue = nullptr; rrc* rrc_enb = nullptr; rrc::mobility_cfg* cfg = nullptr; @@ -90,16 +100,35 @@ private: // vars std::shared_ptr ue_var_meas; - class mobility_proc_t + class sourceenb_ho_proc_t { public: - srslte::proc_outcome_t init() { return srslte::proc_outcome_t::yield; } + explicit sourceenb_ho_proc_t(rrc_mobility* ue_mobility_); + srslte::proc_outcome_t init(const asn1::rrc::meas_id_to_add_mod_s& measid_, + const asn1::rrc::meas_obj_to_add_mod_s& measobj_, + const asn1::rrc::report_cfg_to_add_mod_s& repcfg_, + const asn1::rrc::cells_to_add_mod_s& cell_, + const asn1::rrc::meas_result_eutra_s& meas_res_, + uint32_t target_eci_); srslte::proc_outcome_t step() { return srslte::proc_outcome_t::yield; } + static const char* name() { return "Handover"; } private: - enum class state_t { ho_started }; + // args + rrc_mobility* parent = nullptr; + // run args + const asn1::rrc::meas_id_to_add_mod_s* measid = nullptr; + const asn1::rrc::meas_obj_to_add_mod_s* measobj = nullptr; + const asn1::rrc::report_cfg_to_add_mod_s* repcfg = nullptr; + const asn1::rrc::cells_to_add_mod_s* cell = nullptr; + asn1::rrc::meas_result_eutra_s meas_res; + uint32_t target_eci = 0; + + enum class state_t { ho_preparation } state{}; + ho_interface_t ho_interface{}; + bool fwd_direct_path_available = false; }; - srslte::proc_t mobility_proc; + srslte::proc_t source_ho_proc; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/upper/s1ap.h b/srsenb/hdr/stack/upper/s1ap.h index 5ad28980e..a20784a66 100644 --- a/srsenb/hdr/stack/upper/s1ap.h +++ b/srsenb/hdr/stack/upper/s1ap.h @@ -38,15 +38,14 @@ namespace srsenb { typedef struct { uint32_t rnti; - uint32_t eNB_UE_S1AP_ID; - uint32_t MME_UE_S1AP_ID; - bool release_requested; - uint16_t stream_id; -}ue_ctxt_t; - -class s1ap - :public s1ap_interface_rrc - ,public thread + uint32_t eNB_UE_S1AP_ID; + uint32_t MME_UE_S1AP_ID; + bool release_requested; + uint16_t stream_id; + struct timeval init_timestamp; +} ue_ctxt_t; + +class s1ap : public s1ap_interface_rrc, public thread { public: s1ap(); @@ -105,10 +104,11 @@ private: void build_tai_cgi(); bool connect_mme(); bool setup_s1(); + bool sctp_send_s1ap_pdu(LIBLTE_S1AP_S1AP_PDU_STRUCT* tx_pdu, uint32_t rnti, const char* procedure_name); bool handle_s1ap_rx_pdu(srslte::byte_buffer_t* pdu); - bool handle_initiatingmessage(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg); - bool handle_successfuloutcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg); + bool handle_initiatingmessage(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT* msg); + bool handle_successfuloutcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT* msg); bool handle_unsuccessfuloutcome(LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg); bool handle_paging(LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *msg); @@ -131,14 +131,18 @@ private: bool send_uectxtreleasecomplete(uint16_t rnti, uint32_t mme_ue_id, uint32_t enb_ue_id); bool send_initial_ctxt_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res_); bool send_initial_ctxt_setup_failure(uint16_t rnti); - bool send_erab_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res_); - //bool send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) + bool send_erab_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT* res_); + // bool send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) bool send_uectxmodifyresp(uint16_t rnti); - bool send_uectxmodifyfailure(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT *cause); - - 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); - + bool send_uectxmodifyfailure(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT* cause); + // handover + bool send_ho_required(uint16_t rnti, + uint32_t target_eci, + srslte::plmn_id_t target_plmn, + srslte::unique_byte_buffer_t rrc_container); + + 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); }; } // namespace srsenb diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 9a99f4fed..273c65702 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -1168,13 +1168,13 @@ static int parse_meas_cell_list(rrc_meas_cfg_t* meas_cfg, Setting& root) for (uint32_t i = 0; i < meas_cfg->meas_cells.size(); ++i) { meas_cfg->meas_cells[i].earfcn = root[i]["dl_earfcn"]; meas_cfg->meas_cells[i].pci = (unsigned int)root[i]["pci"] % 504; - meas_cfg->meas_cells[i].cell_id = (unsigned int)root[i]["cell_idx"]; + meas_cfg->meas_cells[i].eci = (unsigned int)root[i]["eci"]; meas_cfg->meas_cells[i].q_offset = 0; // LIBLTE_RRC_Q_OFFSET_RANGE_DB_0; // TODO // // FIXME: TEMP // printf("PARSER: neighbor cell: {dl_earfcn=%d pci=%d cell_idx=0x%x}\n", // meas_cfg->meas_cells[i].earfcn, // meas_cfg->meas_cells[i].pci, - // meas_cfg->meas_cells[i].cell_id); + // meas_cfg->meas_cells[i].eci); } return 0; } diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index db1a7c8f4..3232f771a 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -1212,6 +1212,12 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srslte::unique_byte_buffer_t pdu) state = RRC_STATE_IDLE; } break; + case ul_dcch_msg_type_c::c1_c_::types::meas_report: + printf("Received MEASUREMENT REPORT!\n"); + if (mobility_handler != nullptr) { + mobility_handler->handle_ue_meas_report(ul_dcch_msg.msg.c1().meas_report()); + } + break; default: parent->rrc_log->error("Msg: %s not supported\n", ul_dcch_msg.msg.c1().type().to_string().c_str()); break; @@ -1302,6 +1308,7 @@ bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) parent->rrc_log->error("Failed to unpack EUTRA capabilities message\n"); return false; } + eutra_capabilities_unpacked = true; parent->rrc_log->info("UE rnti: 0x%x category: %d\n", rnti, eutra_capabilities.ue_category); } } @@ -1781,12 +1788,10 @@ void rrc::ue::send_connection_reconf(srslte::unique_byte_buffer_t pdu) conn_reconf->rr_cfg_ded.phys_cfg_ded_present = true; phys_cfg_ded_s* phy_cfg = &conn_reconf->rr_cfg_ded.phys_cfg_ded; - phy_cfg->ant_info_present = true; - phy_cfg->ant_info.set(phys_cfg_ded_s::ant_info_c_::types::explicit_value); - phy_cfg->ant_info.explicit_value() = parent->cfg.antenna_info; - // Configure PHY layer - phy_cfg->cqi_report_cfg_present = true; + phy_cfg->ant_info_present = true; + phy_cfg->ant_info.set_explicit_value() = parent->cfg.antenna_info; + phy_cfg->cqi_report_cfg_present = true; if (parent->cfg.cqi_cfg.mode == RRC_CFG_CQI_MODE_APERIODIC) { phy_cfg->cqi_report_cfg.cqi_report_mode_aperiodic_present = true; if (phy_cfg->ant_info_present and @@ -1797,12 +1802,11 @@ void rrc::ue::send_connection_reconf(srslte::unique_byte_buffer_t pdu) } } else { phy_cfg->cqi_report_cfg.cqi_report_periodic_present = true; - phy_cfg->cqi_report_cfg.cqi_report_periodic.set_setup(); - cqi_get(&phy_cfg->cqi_report_cfg.cqi_report_periodic.setup().cqi_pmi_cfg_idx, - &phy_cfg->cqi_report_cfg.cqi_report_periodic.setup().cqi_pucch_res_idx); - phy_cfg->cqi_report_cfg.cqi_report_periodic.setup().cqi_format_ind_periodic.set( + auto& cqi_rep = phy_cfg->cqi_report_cfg.cqi_report_periodic.set_setup(); + cqi_get(&cqi_rep.cqi_pmi_cfg_idx, &cqi_rep.cqi_pucch_res_idx); + cqi_rep.cqi_format_ind_periodic.set( cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types::wideband_cqi); - phy_cfg->cqi_report_cfg.cqi_report_periodic.setup().simul_ack_nack_and_cqi = parent->cfg.cqi_cfg.simultaneousAckCQI; + cqi_rep.simul_ack_nack_and_cqi = parent->cfg.cqi_cfg.simultaneousAckCQI; if (phy_cfg->ant_info_present and ((phy_cfg->ant_info.explicit_value().tx_mode == ant_info_ded_s::tx_mode_e_::tm3) || (phy_cfg->ant_info.explicit_value().tx_mode == ant_info_ded_s::tx_mode_e_::tm4))) { @@ -1908,6 +1912,7 @@ void rrc::ue::send_connection_reconf(srslte::unique_byte_buffer_t pdu) if (mobility_handler != nullptr) { mobility_handler->fill_conn_recfg_msg(conn_reconf); } + last_rrc_conn_recfg = *conn_reconf; // Reuse same PDU pdu->clear(); @@ -1987,6 +1992,7 @@ void rrc::ue::send_security_mode_command() (ciphering_algorithm_r12_e::options)cipher_algo; comm->crit_exts.c1().security_mode_cmd_r8().security_cfg_smc.security_algorithm_cfg.integrity_prot_algorithm = (security_algorithm_cfg_s::integrity_prot_algorithm_e_::options)integ_algo; + last_security_mode_cmd = comm->crit_exts.c1().security_mode_cmd_r8().security_cfg_smc.security_algorithm_cfg; send_dl_dcch(&dl_dcch_msg); } diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 00cc16c25..0ffade042 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -31,6 +31,12 @@ namespace srsenb { #define Info(fmt, ...) rrc_log->info("Mobility: " fmt, ##__VA_ARGS__) +#define Error(fmt, ...) rrc_log->error("Mobility: " fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) rrc_log->warning("Mobility: " fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) rrc_log->debug("Mobility: " fmt, ##__VA_ARGS__) + +#define procInfo(fmt, ...) parent->rrc_log->info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procError(fmt, ...) parent->rrc_log->error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) using namespace asn1::rrc; @@ -45,6 +51,56 @@ uint32_t eci_to_cellid(uint32_t eci) { return eci & 0xFFu; } +uint16_t compute_mac_i(uint16_t crnti, + uint32_t cellid, + uint16_t pci, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo, + uint8_t* k_rrc_int) +{ + // Compute shortMAC-I + uint8_t varShortMAC_packed[16]; + bzero(varShortMAC_packed, 16); + uint8_t mac_key[4]; + + // ASN.1 encode VarShortMAC-Input + asn1::rrc::var_short_mac_input_s var_short_mac; + var_short_mac.cell_id.from_number(cellid); + var_short_mac.pci = pci; + var_short_mac.c_rnti.from_number(crnti); + + asn1::bit_ref bref(varShortMAC_packed, sizeof(varShortMAC_packed)); + var_short_mac.pack(bref); // already zeroed, so no need to align + uint32_t N_bytes = bref.distance_bytes(); + + printf("Encoded varShortMAC: cellId=0x%x, PCI=%d, rnti=0x%x (%d bytes)\n", cellid, pci, crnti, N_bytes); + + // Compute MAC-I + switch (integ_algo) { + case srslte::INTEGRITY_ALGORITHM_ID_128_EIA1: + srslte::security_128_eia1(&k_rrc_int[16], + 0xffffffff, // 32-bit all to ones + 0x1f, // 5-bit all to ones + 1, // 1-bit to one + varShortMAC_packed, + N_bytes, + mac_key); + break; + case srslte::INTEGRITY_ALGORITHM_ID_128_EIA2: + srslte::security_128_eia2(&k_rrc_int[16], + 0xffffffff, // 32-bit all to ones + 0x1f, // 5-bit all to ones + 1, // 1-bit to one + varShortMAC_packed, + N_bytes, + mac_key); + break; + default: + printf("Unsupported integrity algorithm.\n"); + } + + uint16_t short_mac_i = (((uint16_t)mac_key[2] << 8u) | (uint16_t)mac_key[3]); + return short_mac_i; +} //! convenience function overload to extract Id from MeasObj/MeasId/ReportCfg/Cells constexpr uint8_t get_id(const cells_to_add_mod_s& obj) @@ -96,8 +152,13 @@ using meas_id_cmp = field_id_cmp; template typename Container::iterator binary_find(Container& c, IdType id) { - using item_type = decltype(*Container{}.begin()); - auto it = std::lower_bound(c.begin(), c.end(), id, field_id_cmp{}); + auto it = std::lower_bound(c.begin(), c.end(), id, field_id_cmp{}); + return (it == c.end() or get_id(*it) != id) ? c.end() : it; +} +template +typename Container::const_iterator binary_find(const Container& c, IdType id) +{ + auto it = std::lower_bound(c.begin(), c.end(), id, field_id_cmp{}); return (it == c.end() or get_id(*it) != id) ? c.end() : it; } @@ -242,8 +303,8 @@ var_meas_cfg_t::add_cell_cfg(const meas_cell_cfg_t& cellcfg) using namespace rrc_details; bool inserted_flag = true; - // FIXME: cellcfg.cell_id is the ECI - uint32_t cell_id = rrc_details::eci_to_cellid(cellcfg.cell_id); + // FIXME: cellcfg.eci is the ECI + uint32_t cell_id = rrc_details::eci_to_cellid(cellcfg.eci); q_offset_range_e offset; asn1::number_to_enum(offset, (int8_t)cellcfg.q_offset); // FIXME: What's the difference @@ -264,7 +325,7 @@ var_meas_cfg_t::add_cell_cfg(const meas_cell_cfg_t& cellcfg) inserted_flag = false; } } else { - // cell_id not found. create new cell + // eci not found. create new cell auto& cell_list = ret.first->meas_obj.meas_obj_eutra().cells_to_add_mod_list; cell_list.push_back(new_cell); std::sort(cell_list.begin(), cell_list.end(), rrc_details::cell_id_cmp{}); @@ -586,7 +647,8 @@ rrc::ue::rrc_mobility::rrc_mobility(rrc::ue* outer_ue) : rrc_enb(outer_ue->parent), cfg(outer_ue->parent->enb_mobility_cfg.get()), pool(outer_ue->pool), - rrc_log(outer_ue->parent->rrc_log) + rrc_log(outer_ue->parent->rrc_log), + source_ho_proc(this) { ue_var_meas = std::make_shared(outer_ue->parent->rrc_log); } @@ -594,8 +656,9 @@ rrc::ue::rrc_mobility::rrc_mobility(rrc::ue* outer_ue) : //! Method to add Mobility Info to a RRC Connection Reconfiguration Message bool rrc::ue::rrc_mobility::fill_conn_recfg_msg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg) { - // only reconfigure meas_cfg if no handover is occurring - if (mobility_proc.is_busy()) { + // only reconfigure meas_cfg if no handover is occurring. + // NOTE: We basically freeze ue_var_meas for the whole duration of the handover procedure + if (source_ho_proc.is_busy()) { return false; } @@ -616,4 +679,233 @@ bool rrc::ue::rrc_mobility::fill_conn_recfg_msg(asn1::rrc::rrc_conn_recfg_r8_ies return false; } +//! Method called whenever the eNB receives a MeasReport from the UE. In normal situations, an HO procedure is started +void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg) +{ + const meas_results_s& meas_res = msg.crit_exts.c1().meas_report_r8().meas_results; + + const meas_id_to_add_mod_list_l& l = ue_var_meas->meas_ids(); + auto measid_it = rrc_details::binary_find(l, meas_res.meas_id); + if (measid_it == l.end()) { + Warning("The measurement ID %d provided by the UE does not exist.\n", meas_res.meas_id); + return; + } + if (not meas_res.meas_result_neigh_cells_present) { + Info("Received a MeasReport, but the UE did not detect any cell.\n"); + return; + } + if (meas_res.meas_result_neigh_cells.type().value != + meas_results_s::meas_result_neigh_cells_c_::types::meas_result_list_eutra) { + Error("MeasReports regarding non-EUTRA are not supported!\n"); + return; + } + + const meas_obj_to_add_mod_list_l& objs = ue_var_meas->meas_objs(); + const report_cfg_to_add_mod_list_l& reps = ue_var_meas->rep_cfgs(); + auto obj_it = rrc_details::binary_find(objs, measid_it->meas_obj_id); + auto rep_it = rrc_details::binary_find(reps, measid_it->report_cfg_id); + const meas_result_list_eutra_l& eutra_list = meas_res.meas_result_neigh_cells.meas_result_list_eutra(); + + // iterate from strongest to weakest cell + // NOTE: From now we just look at the strongest. + if (eutra_list.size() > 0) { + uint32_t i = 0; + + uint16_t pci = eutra_list[i].pci; + const cells_to_add_mod_list_l& cells = obj_it->meas_obj.meas_obj_eutra().cells_to_add_mod_list; + + const cells_to_add_mod_s* cell_it = + std::find_if(cells.begin(), cells.end(), [pci](const cells_to_add_mod_s& c) { return c.pci == pci; }); + if (cell_it == cells.end()) { + rrc_log->error("The PCI=%d inside the MeasReport is not recognized.\n", pci); + return; + } + + // eNB found the respective cell. eNB takes "HO Decision" + // TODO: check what to do here to take the decision. + // NOTE: for now just accept anything. + + // HO going forward. + auto& L = rrc_enb->cfg.meas_cfg.meas_cells; + uint32_t target_eci = std::find_if(L.begin(), L.end(), [pci](meas_cell_cfg_t& c) { return c.pci == pci; })->eci; + if (not source_ho_proc.launch(*measid_it, *obj_it, *rep_it, *cell_it, eutra_list[i], target_eci)) { + Error("Failed to start HO procedure, as it is already on-going\n"); + return; + } + } +} + +/** + * Description: Send "HO Required" message from source eNB to MME + * - 1st Message of the handover preparation phase + * - includes info about the target eNB and the radio resources of the source eNB + */ +bool rrc::ue::rrc_mobility::send_s1_ho_required(uint32_t target_eci, uint8_t measobj_id, bool fwd_direct_path_available) +{ + if (fwd_direct_path_available) { + Error("Direct tunnels not supported supported\n"); + return false; + } + + srslte::plmn_id_t target_plmn = + srslte::make_plmn_id_t(rrc_enb->cfg.sib1.cell_access_related_info.plmn_id_list[0].plmn_id); + + /*** Fill HO Preparation Info ***/ + asn1::rrc::ho_prep_info_s hoprep; + asn1::rrc::ho_prep_info_r8_ies_s& hoprep_r8 = hoprep.crit_exts.set_c1().set_ho_prep_info_r8(); + if (not rrc_ue->eutra_capabilities_unpacked) { + // FIXME: temporary. Made up something to please target eNB. (there must be at least one capability in this packet) + hoprep_r8.ue_radio_access_cap_info.resize(1); + hoprep_r8.ue_radio_access_cap_info[0].rat_type = asn1::rrc::rat_type_e::eutra; + asn1::rrc::ue_eutra_cap_s capitem; + capitem.access_stratum_release = asn1::rrc::access_stratum_release_e::rel8; + capitem.ue_category = 4; + capitem.pdcp_params.max_num_rohc_context_sessions_present = true; + capitem.pdcp_params.max_num_rohc_context_sessions = asn1::rrc::pdcp_params_s::max_num_rohc_context_sessions_e_::cs2; + bzero(&capitem.pdcp_params.supported_rohc_profiles, + sizeof(asn1::rrc::rohc_profile_support_list_r15_s)); // FIXME: why is it r15? + capitem.phy_layer_params.ue_specific_ref_sigs_supported = false; + capitem.phy_layer_params.ue_tx_ant_sel_supported = false; + capitem.rf_params.supported_band_list_eutra.resize(1); + capitem.rf_params.supported_band_list_eutra[0].band_eutra = 7; + capitem.rf_params.supported_band_list_eutra[0].half_duplex = false; + capitem.meas_params.band_list_eutra.resize(1); + capitem.meas_params.band_list_eutra[0].inter_rat_band_list_present = false; + capitem.meas_params.band_list_eutra[0].inter_freq_band_list.resize(1); + capitem.meas_params.band_list_eutra[0].inter_freq_band_list[0].inter_freq_need_for_gaps = false; + 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()); + 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()); + } + Debug("UE RA Category: %d\n", capitem.ue_category); + } else { + hoprep_r8.ue_radio_access_cap_info.resize(1); + for (ue_cap_rat_container_s& ratcntr : hoprep_r8.ue_radio_access_cap_info) { + ratcntr.rat_type = asn1::rrc::rat_type_e::eutra; + asn1::bit_ref bref(&ratcntr.ue_cap_rat_container[0], ratcntr.ue_cap_rat_container.size()); + rrc_ue->eutra_capabilities.pack(bref); + } + } + /*** fill AS-Config ***/ + hoprep_r8.as_cfg_present = true; + // NOTE: set source_meas_cnfg equal to the UE's current var_meas_cfg + var_meas_cfg_t empty_meascfg{rrc_log}, target_var_meas = *ue_var_meas; + // // however, reset the MeasObjToAdd Cells, so that the UE does not measure again the target eNB + // meas_obj_to_add_mod_s* obj = rrc_details::binary_find(target_var_meas.meas_objs(), measobj_id); + // obj->meas_obj.meas_obj_eutra().cells_to_add_mod_list.resize(0); + empty_meascfg.compute_diff_meas_cfg(target_var_meas, &hoprep_r8.as_cfg.source_meas_cfg); + // - fill source RR Config + hoprep_r8.as_cfg.source_rr_cfg.sps_cfg_present = false; // TODO: CHECK + hoprep_r8.as_cfg.source_rr_cfg.mac_main_cfg_present = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.mac_main_cfg_present; + hoprep_r8.as_cfg.source_rr_cfg.mac_main_cfg = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.mac_main_cfg; + hoprep_r8.as_cfg.source_rr_cfg.phys_cfg_ded_present = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.phys_cfg_ded_present; + hoprep_r8.as_cfg.source_rr_cfg.phys_cfg_ded = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.phys_cfg_ded; + // Add SRB2 to the message + hoprep_r8.as_cfg.source_rr_cfg.srb_to_add_mod_list_present = + rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.srb_to_add_mod_list_present; + hoprep_r8.as_cfg.source_rr_cfg.srb_to_add_mod_list = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.srb_to_add_mod_list; + // hoprep_r8.as_cfg.source_rr_cfg.srb_to_add_mod_list_present = true; + // asn1::rrc::srb_to_add_mod_list_l& srb_list = hoprep_r8.as_cfg.source_rr_cfg.srb_to_add_mod_list; + // srb_list.resize(1); + // srb_list[0].srb_id = 2; + // srb_list[0].lc_ch_cfg_present = true; + // srb_list[0].lc_ch_cfg.set(asn1::rrc::srb_to_add_mod_s::lc_ch_cfg_c_::types::default_value); + // srb_list[0].rlc_cfg_present = true; + // srb_list[0].rlc_cfg.set_explicit_value(); + // auto& am = srb_list[0].rlc_cfg.explicit_value().set_am(); // FIXME: Which rlc cfg??? I took from a pcap for now + // am.ul_am_rlc.t_poll_retx = asn1::rrc::t_poll_retx_e::ms60; + // am.ul_am_rlc.poll_pdu = asn1::rrc::poll_pdu_e::p_infinity; + // am.ul_am_rlc.poll_byte.value = asn1::rrc::poll_byte_e::kbinfinity; + // am.ul_am_rlc.max_retx_thres.value = asn1::rrc::ul_am_rlc_s::max_retx_thres_e_::t32; + // am.dl_am_rlc.t_reordering.value = asn1::rrc::t_reordering_e::ms45; + // am.dl_am_rlc.t_status_prohibit.value = asn1::rrc::t_status_prohibit_e::ms0; + // Get DRB1 configuration + hoprep_r8.as_cfg.source_rr_cfg.drb_to_add_mod_list_present = + rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.drb_to_add_mod_list_present; + hoprep_r8.as_cfg.source_rr_cfg.drb_to_add_mod_list = rrc_ue->last_rrc_conn_recfg.rr_cfg_ded.drb_to_add_mod_list; + // hoprep_r8.as_cfg.source_rr_cfg.drb_to_add_mod_list_present = true; + // asn1::rrc::drb_to_add_mod_list_l& drb_list = hoprep_r8.as_cfg.source_rr_cfg.drb_to_add_mod_list; + // drb_list.resize(1); + // rrc_ue->get_drbid_config(&hoprep_r8.as_cfg.source_rr_cfg.drb_to_add_mod_list[0], 1); + // hoprep_r8.as_cfg.source_rr_cfg.drb_to_release_list_present = true; + // hoprep_r8.as_cfg.source_rr_cfg.drb_to_release_list.resize(1); + // hoprep_r8.as_cfg.source_rr_cfg.drb_to_release_list[0] = 1; + hoprep_r8.as_cfg.source_security_algorithm_cfg = rrc_ue->last_security_mode_cmd; + hoprep_r8.as_cfg.source_ue_id.from_number(rrc_ue->rnti); + asn1::number_to_enum(hoprep_r8.as_cfg.source_mib.dl_bw, rrc_enb->cfg.cell.nof_prb); + hoprep_r8.as_cfg.source_mib.phich_cfg.phich_dur.value = + (asn1::rrc::phich_cfg_s::phich_dur_e_::options)rrc_enb->cfg.cell.phich_length; + hoprep_r8.as_cfg.source_mib.phich_cfg.phich_res.value = + (asn1::rrc::phich_cfg_s::phich_res_e_::options)rrc_enb->cfg.cell.phich_resources; + hoprep_r8.as_cfg.source_mib.sys_frame_num.from_number(0); // NOTE: The TS says this can go empty + hoprep_r8.as_cfg.source_sib_type1 = rrc_enb->cfg.sib1; + hoprep_r8.as_cfg.source_sib_type2 = rrc_enb->sib2; + asn1::number_to_enum(hoprep_r8.as_cfg.ant_info_common.ant_ports_count, rrc_enb->cfg.cell.nof_ports); + hoprep_r8.as_cfg.source_dl_carrier_freq = rrc_enb->cfg.dl_earfcn; + // - fill as_context + hoprep_r8.as_context_present = true; + hoprep_r8.as_context.reest_info_present = true; + hoprep_r8.as_context.reest_info.source_pci = rrc_enb->cfg.pci; + hoprep_r8.as_context.reest_info.target_cell_short_mac_i.from_number( + rrc_details::compute_mac_i(rrc_ue->rnti, + rrc_enb->cfg.sib1.cell_access_related_info.cell_id.to_number(), + rrc_enb->cfg.pci, + rrc_ue->integ_algo, + rrc_ue->k_rrc_int)); + + /*** pack HO Preparation Info into an RRC container buffer ***/ + srslte::unique_byte_buffer_t buffer = srslte::allocate_unique_buffer(*pool); + asn1::bit_ref bref(buffer->msg, buffer->get_tailroom()); + if (hoprep.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { + Error("Failed to pack HO preparation msg\n"); + return false; + } + buffer->N_bytes = bref.distance_bytes(); + + bool success = rrc_enb->s1ap->send_ho_required(rrc_ue->rnti, target_eci, target_plmn, std::move(buffer)); + Info("sent s1ap msg with HO Required\n"); + return success; +} + +/************************************************************************************************* + * sourceenb_ho_proc_t class + ************************************************************************************************/ + +rrc::ue::rrc_mobility::sourceenb_ho_proc_t::sourceenb_ho_proc_t(rrc_mobility* ue_mobility_) : parent(ue_mobility_) {} + +srslte::proc_outcome_t rrc::ue::rrc_mobility::sourceenb_ho_proc_t::init(const meas_id_to_add_mod_s& measid_, + const meas_obj_to_add_mod_s& measobj_, + const report_cfg_to_add_mod_s& repcfg_, + const cells_to_add_mod_s& cell_, + const meas_result_eutra_s& meas_res_, + uint32_t target_eci_) +{ + measid = &measid_; + measobj = &measobj_; + repcfg = &repcfg_; + cell = &cell_; + meas_res = meas_res_; + target_eci = target_eci_; + + // TODO: Check X2 is available first. If fail, go for S1. + // NOTE: For now only S1-HO is supported. X2 also not available for fwd direct path + ho_interface = ho_interface_t::S1; + fwd_direct_path_available = false; + + state = state_t::ho_preparation; + procInfo("Started Handover of rnti=0x%x to %s.\n", parent->rrc_ue->rnti, rrc_details::to_string(*cell).c_str()); + if (not parent->send_s1_ho_required(target_eci, measobj->meas_obj_id, fwd_direct_path_available)) { + procError("Failed to send HO Required to MME.\n"); + return srslte::proc_outcome_t::error; + } + return srslte::proc_outcome_t::yield; +} + } // namespace srsenb diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index c94c3b059..0baaeeb79 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -188,9 +188,10 @@ 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].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; send_initialuemessage(rnti, cause, std::move(pdu), false); } @@ -1146,7 +1147,126 @@ bool s1ap::send_uectxmodifyfailure(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT* caus return true; } -//bool s1ap::send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) +/********************* + * Handover Messages + ********************/ + +bool s1ap::send_ho_required(uint16_t rnti, + uint32_t target_eci, + srslte::plmn_id_t target_plmn, + srslte::unique_byte_buffer_t rrc_container) +{ + 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"); + 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"); +} + +// bool s1ap::send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) //{ // srslte::byte_buffer_t msg; @@ -1182,13 +1302,40 @@ bool s1ap::send_uectxmodifyfailure(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT* caus /* General helpers ********************************************************************************/ -bool s1ap::find_mme_ue_id(uint32_t mme_ue_id, uint16_t *rnti, uint32_t *enb_ue_id) +bool s1ap::sctp_send_s1ap_pdu(LIBLTE_S1AP_S1AP_PDU_STRUCT* tx_pdu, uint32_t rnti, const char* procedure_name) +{ + srslte::unique_byte_buffer_t buf = srslte::allocate_unique_buffer(*pool, false); + if (buf == nullptr) { + s1ap_log->error("Fatal Error: Couldn't allocate buffer for %s.\n", procedure_name); + return false; + } + + liblte_s1ap_pack_s1ap_pdu(tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get()); + s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending %s for rnti=0x%x", procedure_name, 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); + if (n_sent == -1) { + s1ap_log->error("Failed to send %s for rnti=0x%x\n", procedure_name, rnti); + return false; + } + + return true; +} + +bool s1ap::find_mme_ue_id(uint32_t mme_ue_id, uint16_t* rnti, uint32_t* enb_ue_id) { - typedef std::map::iterator it_t; - for(it_t it=ue_ctxt_map.begin(); it!=ue_ctxt_map.end(); ++it) { - 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 : 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; return true; } } diff --git a/srsenb/test/upper/rrc_mobility_test.cc b/srsenb/test/upper/rrc_mobility_test.cc index ce41932c4..54f5be2a7 100644 --- a/srsenb/test/upper/rrc_mobility_test.cc +++ b/srsenb/test/upper/rrc_mobility_test.cc @@ -59,7 +59,7 @@ meas_cell_cfg_t generate_cell1() cell1.earfcn = 3400; cell1.pci = 1; cell1.q_offset = 0; - cell1.cell_id = 0x19C01; + cell1.eci = 0x19C01; return cell1; } @@ -82,7 +82,7 @@ report_cfg_eutra_s generate_rep1() bool is_cell_cfg_equal(const meas_cell_cfg_t& cfg, const cells_to_add_mod_s& cell) { return cfg.pci == cell.pci and cell.cell_individual_offset.to_number() == (int8_t)round(cfg.q_offset) and - cell.cell_idx == (cfg.cell_id & 0xFFu); + cell.cell_idx == (cfg.eci & 0xFFu); } int test_correct_insertion() @@ -90,7 +90,7 @@ int test_correct_insertion() meas_cell_cfg_t cell1 = generate_cell1(), cell2{}, cell3{}, cell4{}; cell2 = cell1; cell2.pci = 2; - cell2.cell_id = 0x19C02; + cell2.eci = 0x19C02; cell3 = cell1; cell3.earfcn = 2850; cell4 = cell1; @@ -129,8 +129,8 @@ int test_correct_insertion() TESTASSERT(eutra.carrier_freq == cell1.earfcn); TESTASSERT(eutra.cells_to_add_mod_list.size() == 2); const cells_to_add_mod_s* cell_it = eutra.cells_to_add_mod_list.begin(); - TESTASSERT(cell_it[0].cell_idx == (cell1.cell_id & 0xFFu)); - TESTASSERT(cell_it[1].cell_idx == (cell2.cell_id & 0xFFu)); + TESTASSERT(cell_it[0].cell_idx == (cell1.eci & 0xFFu)); + TESTASSERT(cell_it[1].cell_idx == (cell2.eci & 0xFFu)); TESTASSERT(cell_it[1].pci == cell2.pci); // TEST 3: insertion of cell in another frequency @@ -148,7 +148,7 @@ int test_correct_insertion() TESTASSERT(objs.size() == 2 and objs[0].meas_obj_id == 1); TESTASSERT(eutra3.carrier_freq == cell4.earfcn); TESTASSERT(eutra3.cells_to_add_mod_list.size() == 2); - TESTASSERT(eutra3.cells_to_add_mod_list[0].cell_idx == (cell1.cell_id & 0xFFu)); + TESTASSERT(eutra3.cells_to_add_mod_list[0].cell_idx == (cell1.eci & 0xFFu)); TESTASSERT(eutra3.cells_to_add_mod_list[0].cell_individual_offset.to_number() == 1); } @@ -163,10 +163,10 @@ int test_correct_meascfg_calculation() cell1.earfcn = 3400; cell1.pci = 1; cell1.q_offset = 0; - cell1.cell_id = 0x19C01; + cell1.eci = 0x19C01; cell2 = cell1; cell2.pci = 2; - cell2.cell_id = 0x19C02; + cell2.eci = 0x19C02; report_cfg_eutra_s rep1 = generate_rep1(), rep2{}, rep3{}; rep2 = rep1; @@ -250,7 +250,7 @@ int test_correct_meascfg_calculation() eutra = item->meas_obj.meas_obj_eutra(); TESTASSERT(not eutra.cells_to_add_mod_list_present and eutra.cells_to_rem_list_present); TESTASSERT(eutra.cells_to_rem_list.size() == 1); - TESTASSERT(eutra.cells_to_rem_list[0] == (cell1.cell_id & 0xFFu)); + TESTASSERT(eutra.cells_to_rem_list[0] == (cell1.eci & 0xFFu)); TESTASSERT(result_meascfg.report_cfg_to_add_mod_list_present and not result_meascfg.report_cfg_to_rem_list_present); TESTASSERT(result_meascfg.report_cfg_to_add_mod_list.size() == 1); TESTASSERT(result_meascfg.report_cfg_to_add_mod_list[0].report_cfg_id == 2);