diff --git a/lib/include/srslte/interfaces/enb_interfaces.h b/lib/include/srslte/interfaces/enb_interfaces.h index 9b7e5c934..26c194198 100644 --- a/lib/include/srslte/interfaces/enb_interfaces.h +++ b/lib/include/srslte/interfaces/enb_interfaces.h @@ -299,7 +299,7 @@ public: * Allocate a C-RNTI for a new user, without adding it to the phy layer and scheduler yet * @return value of the allocated C-RNTI */ - virtual uint16_t allocate_rnti() = 0; + virtual uint16_t reserve_new_crnti(const sched_interface::ue_cfg_t& ue_cfg) = 0; }; class mac_interface_rlc @@ -435,6 +435,10 @@ public: * @param container TargeteNB RRCConnectionReconfiguration message with MobilityControlInfo */ virtual void ho_preparation_complete(uint16_t rnti, bool is_success, srslte::unique_byte_buffer_t container) = 0; + virtual uint16_t + start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg, + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + srslte::byte_buffer_t& ho_cmd) = 0; }; // GTPU interface for PDCP diff --git a/lib/src/asn1/s1ap_asn1.cc b/lib/src/asn1/s1ap_asn1.cc index ab8db2a4c..3446bcc72 100644 --- a/lib/src/asn1/s1ap_asn1.cc +++ b/lib/src/asn1/s1ap_asn1.cc @@ -397,6 +397,7 @@ bool protocol_ie_single_container_s::load_info_obj(const uint32 value = ies_set_paramT_::get_value(id); return value.type().value != ies_set_paramT_::value_c::types_opts::nulltype; } +template bool protocol_ie_single_container_s::load_info_obj(const uint32_t& id_); // ProtocolIE-FieldPair{S1AP-PROTOCOL-IES-PAIR : IEsSetParam} ::= SEQUENCE{{S1AP-PROTOCOL-IES-PAIR}} template diff --git a/srsenb/hdr/stack/mac/mac.h b/srsenb/hdr/stack/mac/mac.h index 486221733..46f41f841 100644 --- a/srsenb/hdr/stack/mac/mac.h +++ b/srsenb/hdr/stack/mac/mac.h @@ -93,19 +93,20 @@ public: int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) override; int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) override; + /* Handover-related */ + uint16_t reserve_new_crnti(const sched_interface::ue_cfg_t& ue_cfg) override; + bool process_pdus(); void get_metrics(mac_metrics_t metrics[ENB_METRICS_MAX_USERS]); void write_mcch(asn1::rrc::sib_type2_s* sib2, asn1::rrc::sib_type13_r9_s* sib13, asn1::rrc::mcch_msg_s* mcch) override; - /* Allocate C-RNTI */ - uint16_t allocate_rnti() final; - private: static const uint32_t cfi = 3; - bool check_ue_exists(uint16_t rnti); + bool check_ue_exists(uint16_t rnti); + uint16_t allocate_rnti(); std::mutex rnti_mutex; diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index 76ab88ad6..56384a7af 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -80,14 +80,17 @@ public: void max_retx_attempted(uint16_t rnti) override; // rrc_interface_s1ap - void write_dl_info(uint16_t rnti, srslte::unique_byte_buffer_t sdu) override; - void release_complete(uint16_t rnti) override; - bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) override; - bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) override; - bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) override; - bool release_erabs(uint32_t rnti) override; - void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& UEPagingID) override; - void ho_preparation_complete(uint16_t rnti, bool is_success, srslte::unique_byte_buffer_t rrc_container) override; + void write_dl_info(uint16_t rnti, srslte::unique_byte_buffer_t sdu) override; + void release_complete(uint16_t rnti) override; + bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) override; + bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) override; + bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) override; + bool release_erabs(uint32_t rnti) override; + void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& UEPagingID) override; + void ho_preparation_complete(uint16_t rnti, bool is_success, srslte::unique_byte_buffer_t rrc_container) override; + uint16_t start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg, + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + srslte::byte_buffer_t& ho_cmd) override; // rrc_interface_pdcp void write_pdu(uint16_t rnti, uint32_t lcid, srslte::unique_byte_buffer_t pdu) override; @@ -95,7 +98,7 @@ public: uint32_t get_nof_users(); // logging - typedef enum { Rx = 0, Tx } direction_t; + typedef enum { Rx = 0, Tx, S1AP } direction_t; template void log_rrc_message(const std::string& source, const direction_t dir, diff --git a/srsenb/hdr/stack/rrc/rrc_mobility.h b/srsenb/hdr/stack/rrc/rrc_mobility.h index c901de137..b7af6ae1b 100644 --- a/srsenb/hdr/stack/rrc/rrc_mobility.h +++ b/srsenb/hdr/stack/rrc/rrc_mobility.h @@ -86,6 +86,10 @@ public: rrc* get_rrc() { return rrc_ptr; } const rrc* get_rrc() const { return rrc_ptr; } + uint16_t start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg, + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + srslte::byte_buffer_t& ho_cmd); + private: // args rrc* rrc_ptr = nullptr; @@ -107,6 +111,11 @@ public: void handle_ho_preparation_complete(bool is_success, srslte::unique_byte_buffer_t container); bool is_ho_running() const { return not is_in_state(); } + // S1-Handover + bool start_s1_tenb_ho(const asn1::s1ap::ho_request_s& msg, + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + srslte::byte_buffer_t& ho_cmd); + private: // Handover from source cell bool start_ho_preparation(uint32_t target_eci, uint8_t measobj_id, bool fwd_direct_path_available); diff --git a/srsenb/hdr/stack/upper/s1ap.h b/srsenb/hdr/stack/upper/s1ap.h index 80b5b1d70..3b7e6ff1c 100644 --- a/srsenb/hdr/stack/upper/s1ap.h +++ b/srsenb/hdr/stack/upper/s1ap.h @@ -81,6 +81,8 @@ public: srslte::plmn_id_t target_plmn, srslte::unique_byte_buffer_t rrc_container) override; bool send_enb_status_transfer_proc(uint16_t rnti, std::vector& bearer_status_list) override; + bool send_ho_failure(uint32_t mme_ue_s1ap_id); + bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, uint16_t rnti, srslte::unique_byte_buffer_t ho_cmd); // void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps); // Stack interface @@ -145,6 +147,7 @@ private: // handover bool handle_hopreparationfailure(const asn1::s1ap::ho_prep_fail_s& msg); bool handle_s1hocommand(const asn1::s1ap::ho_cmd_s& msg); + bool handle_ho_request(const asn1::s1ap::ho_request_s& msg); // UE-specific data and procedures struct ue { @@ -193,9 +196,6 @@ private: ue_ctxt_t ctxt = {}; uint16_t stream_id = 1; - // user procedures - srslte::proc_t 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); @@ -209,6 +209,10 @@ private: bool release_requested = false; srslte::unique_timer ts1_reloc_prep; ///< TS1_{RELOCprep} - max time for HO preparation srslte::unique_timer ts1_reloc_overall; ///< TS1_{RELOCOverall} + + public: + // user procedures + srslte::proc_t ho_prep_proc; }; class user_list diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index 0ae6fd8cc..97989c5bc 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -443,6 +443,46 @@ uint16_t mac::allocate_rnti() return rnti; } +uint16_t mac::reserve_new_crnti(const sched_interface::ue_cfg_t& ue_cfg) +{ + // Get pre-allocated UE object + if (ue_pool.empty()) { + Error("Ignoring RACH attempt. UE pool empty.\n"); + return SRSLTE_INVALID_RNTI; + } + auto ue_ptr = ue_pool.wait_pop(); + uint16_t rnti = ue_ptr->get_rnti(); + + // Set PCAP if available + if (pcap != nullptr) { + ue_ptr->start_pcap(pcap); + } + + { + srslte::rwlock_write_guard lock(rwlock); + ue_db[rnti] = std::move(ue_ptr); + } + + // Add new user to the scheduler so that it can RX/TX SRB0 + if (scheduler.ue_cfg(rnti, ue_cfg) != SRSLTE_SUCCESS) { + Error("Registering new user rnti=0x%x to SCHED\n", rnti); + return SRSLTE_INVALID_RNTI; + } + + // Register new user in RRC + rrc_h->add_user(rnti, ue_cfg); + + // Add temporal rnti to the PHY + if (phy_h->add_rnti(rnti, ue_cfg.supported_cc_list[0].enb_cc_idx) != SRSLTE_SUCCESS) { + Error("Registering c-rnti=0x%x to PHY\n", rnti); + return SRSLTE_INVALID_RNTI; + } + + // Allocate one new UE object in advance + prealloc_ue(1); + return rnti; +} + void mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx, uint32_t time_adv) { static srslte::mutexed_tprof rach_tprof("rach_tprof", "MAC", 1); diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index 8ecc87d96..8c467321a 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -462,6 +462,13 @@ void rrc::ho_preparation_complete(uint16_t rnti, bool is_success, srslte::unique users.at(rnti)->mobility_handler->handle_ho_preparation_complete(is_success, std::move(rrc_container)); } +uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg, + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + srslte::byte_buffer_t& ho_cmd) +{ + return enb_mobility_cfg->start_ho_ue_resource_alloc(msg, container, ho_cmd); +} + /******************************************************************************* Private functions All private functions are not mutexed and must be called from a mutexed environment diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 661963414..13905fbc9 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -409,6 +409,18 @@ var_meas_cfg_t var_meas_cfg_t::make(const asn1::rrc::meas_cfg_s& meas_cfg) var.var_meas.quant_cfg_present = true; var.var_meas.quant_cfg = meas_cfg.quant_cfg; } + if (meas_cfg.s_measure_present) { + var.var_meas.s_measure_present = true; + var.var_meas.s_measure = meas_cfg.s_measure; + } + if (meas_cfg.speed_state_pars_present) { + var.var_meas.speed_state_pars_present = true; + var.var_meas.speed_state_pars.set(meas_cfg.speed_state_pars.type().value); + if (var.var_meas.speed_state_pars.type().value == setup_opts::setup) { + var.var_meas.speed_state_pars.setup().mob_state_params = meas_cfg.speed_state_pars.setup().mob_state_params; + var.var_meas.speed_state_pars.setup().time_to_trigger_sf = meas_cfg.speed_state_pars.setup().time_to_trigger_sf; + } + } if (meas_cfg.report_cfg_to_rem_list_present or meas_cfg.meas_obj_to_rem_list_present or meas_cfg.meas_id_to_rem_list_present) { srslte::logmap::get("RRC")->warning("Remove lists not handled by the var_meas_cfg_t method\n"); @@ -456,6 +468,54 @@ rrc::enb_mobility_handler::enb_mobility_handler(rrc* rrc_) : rrc_ptr(rrc_), cfg( } } +uint16_t rrc::enb_mobility_handler::start_ho_ue_resource_alloc( + const asn1::s1ap::ho_request_s& msg, + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + srslte::byte_buffer_t& ho_cmd) +{ + // TODO: Decision Making on whether the same QoS of the source eNB can be provided by target eNB + + /* Evaluate if cell exists */ + uint32_t target_eci = container.target_cell_id.cell_id.to_number(); + const cell_info_common* target_cell = rrc_ptr->cell_common_list->get_cell_id(rrc_details::eci_to_cellid(target_eci)); + if (target_cell == nullptr) { + rrc_ptr->rrc_log->error("The S1-handover target cell_id=0x%x does not exist\n", + rrc_details::eci_to_cellid(target_eci)); + return SRSLTE_INVALID_RNTI; + } + + /* Allocate C-RNTI */ + sched_interface::ue_cfg_t ue_cfg = {}; + ue_cfg.supported_cc_list.resize(1); + ue_cfg.supported_cc_list[0].active = true; + ue_cfg.supported_cc_list[0].enb_cc_idx = target_cell->enb_cc_idx; + ue_cfg.ue_bearers[0].direction = sched_interface::ue_bearer_cfg_t::BOTH; + ue_cfg.supported_cc_list[0].dl_cfg.tm = SRSLTE_TM1; + uint16_t rnti = rrc_ptr->mac->reserve_new_crnti(ue_cfg); + if (rnti == SRSLTE_INVALID_RNTI) { + rrc_ptr->rrc_log->error("Failed to allocate C-RNTI resources\n"); + return SRSLTE_INVALID_RNTI; + } + + // /* Setup e-RABs & DRBs / establish an UL/DL S1 bearer to the S-GW */ + // if (not setup_ue_erabs(rnti, msg)) { + // rrc_ptr->rrc_log->error("Failed to setup e-RABs for rnti=0x%x\n", ); + // } + + // TODO: KeNB derivations + + auto it = rrc_ptr->users.find(rnti); + if (it == rrc_ptr->users.end()) { + rrc_ptr->rrc_log->error("Did not find rnti=0x%x in ue_db\n", rnti); + return SRSLTE_INVALID_RNTI; + } + ue* ue_ptr = it->second.get(); + if (not ue_ptr->mobility_handler->start_s1_tenb_ho(msg, container, ho_cmd)) { + return SRSLTE_INVALID_RNTI; + } + return rnti; +} + /************************************************************************************************* * rrc_mobility class ************************************************************************************************/ @@ -529,9 +589,10 @@ void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg) continue; } meas_ev.meas_cell = cell_it; - meas_ev.target_eci = std::find_if(meas_list_cfg.begin(), meas_list_cfg.end(), [pci](const meas_cell_cfg_t& c) { - return c.pci == pci; - })->eci; + meas_ev.target_eci = std::find_if(meas_list_cfg.begin(), + meas_list_cfg.end(), + [pci](const meas_cell_cfg_t& c) { return c.pci == pci; }) + ->eci; // eNB found the respective cell. eNB takes "HO Decision" // NOTE: From now we just choose the strongest. @@ -713,6 +774,70 @@ void rrc::ue::rrc_mobility::handle_ho_preparation_complete(bool is_success, srsl trigger(std::move(container)); } +/** + * Description: Handover Requested Acknowledgment (with success or failure) + * - TeNB --> MME + * - Response from TeNB on whether it was able to allocate resources for user doing handover + * - Preparation of HandoverCommand that goes inside the transparent container + * @return rnti of created ue + */ +bool rrc::ue::rrc_mobility::start_s1_tenb_ho( + const asn1::s1ap::ho_request_s& msg, + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + srslte::byte_buffer_t& ho_cmd_pdu) +{ + const cell_ctxt_dedicated* target_cell = rrc_ue->cell_ded_list.get_ue_cc_idx(UE_PCELL_CC_IDX); + + /* TS 36.331 10.2.2. - Decode HandoverPreparationInformation */ + asn1::cbit_ref bref{container.rrc_container.data(), container.rrc_container.size()}; + asn1::rrc::ho_prep_info_s hoprep; + if (hoprep.unpack(bref) != asn1::SRSASN_SUCCESS) { + rrc_enb->rrc_log->error("Failed to decode HandoverPreparatiobinformation in S1AP SourceENBToTargetENBContainer\n"); + return false; + } + if (hoprep.crit_exts.type().value != c1_or_crit_ext_opts::c1 or + hoprep.crit_exts.c1().type().value != ho_prep_info_s::crit_exts_c_::c1_c_::types_opts::ho_prep_info_r8) { + rrc_enb->rrc_log->error("Only release 8 supported\n"); + return false; + } + const ho_prep_info_r8_ies_s& hoprep_r8 = hoprep.crit_exts.c1().ho_prep_info_r8(); + + /* Prepare Handover Request Acknowledgment - Handover Command */ + dl_dcch_msg_s dl_dcch_msg; + rrc_conn_recfg_r8_ies_s& recfg_r8 = + dl_dcch_msg.msg.set_c1().set_rrc_conn_recfg().crit_exts.set_c1().set_rrc_conn_recfg_r8(); + + // Fill fields common to all types of handover + fill_mobility_reconf_common(dl_dcch_msg, *target_cell->cell_common); + + // Encode MeasConfig + var_meas_cfg_t current_var_meas = var_meas_cfg_t::make(hoprep_r8.as_cfg.source_meas_cfg); + recfg_r8.meas_cfg_present = + update_ue_var_meas_cfg(current_var_meas, target_cell->cell_common->enb_cc_idx, &recfg_r8.meas_cfg); + + /* Prepare Handover Command to be sent via S1AP */ + asn1::bit_ref bref2{ho_cmd_pdu.msg, ho_cmd_pdu.get_tailroom()}; + if (dl_dcch_msg.pack(bref2) != asn1::SRSASN_SUCCESS) { + rrc_enb->rrc_log->error("Failed to pack HandoverCommand\n"); + return false; + } + ho_cmd_pdu.N_bytes = bref2.distance_bytes(); + rrc_enb->log_rrc_message("RRC container", direction_t::S1AP, &ho_cmd_pdu, dl_dcch_msg, "HandoverCommand"); + + asn1::rrc::ho_cmd_s ho_cmd; + asn1::rrc::ho_cmd_r8_ies_s& ho_cmd_r8 = ho_cmd.crit_exts.set_c1().set_ho_cmd_r8(); + ho_cmd_r8.ho_cmd_msg.resize(bref2.distance_bytes()); + memcpy(ho_cmd_r8.ho_cmd_msg.data(), ho_cmd_pdu.msg, bref2.distance_bytes()); + bref2 = {ho_cmd_pdu.msg, ho_cmd_pdu.get_tailroom()}; + if (ho_cmd.pack(bref2) != asn1::SRSASN_SUCCESS) { + rrc_enb->rrc_log->error("Failed to pack HandoverCommand\n"); + return false; + } + ho_cmd_pdu.N_bytes = bref2.distance_bytes(); + + return true; +} + bool rrc::ue::rrc_mobility::update_ue_var_meas_cfg(const asn1::rrc::meas_cfg_s& source_meas_cfg, uint32_t target_enb_cc_idx, asn1::rrc::meas_cfg_s* diff_meas_cfg) @@ -740,6 +865,18 @@ bool rrc::ue::rrc_mobility::update_ue_var_meas_cfg(const var_meas_cfg_t& source return meas_cfg_present; } +/** + * @brief Fills RRCConnectionReconfigurationMessage with Handover Command fields that are common to + * all types of handover (e.g. S1, intra-enb, X2), namely: + * - mobilityControlInformation + * - SecurityConfigHandover + * - RadioReconfiguration.PhyConfig + * - Scheduling Request setup + * - CQI report cfg + * - AntennaConfig + * @param msg + * @param target_cell + */ void rrc::ue::rrc_mobility::fill_mobility_reconf_common(asn1::rrc::dl_dcch_msg_s& msg, const cell_info_common& target_cell) { @@ -760,6 +897,7 @@ void rrc::ue::rrc_mobility::fill_mobility_reconf_common(asn1::rrc::dl_dcch_msg_s mob_info.rr_cfg_common.p_max_present = true; mob_info.rr_cfg_common.p_max = rrc_enb->cfg.sib1.p_max; mob_info.carrier_freq_present = false; // same frequency handover for now + asn1::number_to_enum(mob_info.carrier_bw.dl_bw, target_cell.mib.dl_bw.to_number()); // Set security cfg recfg_r8.security_cfg_ho_present = true; @@ -863,6 +1001,7 @@ bool rrc::ue::rrc_mobility::needs_intraenb_ho(idle_st& s, const ho_meas_report_e void rrc::ue::rrc_mobility::s1_source_ho_st::wait_ho_req_ack_st::enter(s1_source_ho_st* f, const ho_meas_report_ev& ev) { + f->log_h->console("Starting S1 Handover of rnti=0x%x to 0x%x.\n", f->parent_fsm()->rrc_ue->rnti, ev.target_eci); f->log_h->info("Starting S1 Handover of rnti=0x%x to 0x%x.\n", f->parent_fsm()->rrc_ue->rnti, ev.target_eci); f->report = ev; diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index 9d1b1062a..30f459f3e 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -559,6 +559,13 @@ bool s1ap::handle_initiatingmessage(const init_msg_s& msg) return handle_erabsetuprequest(msg.value.erab_setup_request()); case s1ap_elem_procs_o::init_msg_c::types_opts::ue_context_mod_request: return handle_uecontextmodifyrequest(msg.value.ue_context_mod_request()); + case s1ap_elem_procs_o::init_msg_c::types_opts::ho_request: { + bool outcome = handle_ho_request(msg.value.ho_request()); + if (not outcome) { + send_ho_failure(msg.value.ho_request().protocol_ies.mme_ue_s1ap_id.value.value); + } + return outcome; + } default: s1ap_log->error("Unhandled initiating message: %s\n", msg.value.type().to_string().c_str()); } @@ -790,6 +797,106 @@ bool s1ap::handle_s1hocommand(const asn1::s1ap::ho_cmd_s& msg) return true; } +/************************************************************** + * TS 36.413 - Section 8.4.2 - "Handover Resource Allocation" + *************************************************************/ + +bool s1ap::handle_ho_request(const asn1::s1ap::ho_request_s& msg) +{ + s1ap_log->info("Received S1 HO Request\n"); + s1ap_log->console("Received S1 HO Request\n"); + + if (msg.ext or msg.protocol_ies.ho_restrict_list_present or + msg.protocol_ies.handov_type.value.value != handov_type_opts::intralte) { + s1ap_log->error("Not handling S1AP non-intra LTE handovers and extensions\n"); + return false; + } + + // Confirm the UE does not exist in TeNB + if (users.find_ue_mmeid(msg.protocol_ies.mme_ue_s1ap_id.value.value) != nullptr) { + s1ap_log->error("The provided MME_UE_S1AP_ID=%ld is already connected to the cell\n", + msg.protocol_ies.mme_ue_s1ap_id.value.value); + return false; + } + + // Create user ctxt object and associated MME context + std::unique_ptr ue_ptr{new ue{this}}; + ue_ptr->ctxt.mme_ue_s1ap_id_present = true; + ue_ptr->ctxt.mme_ue_s1ap_id = msg.protocol_ies.mme_ue_s1ap_id.value.value; + if (users.add_user(std::move(ue_ptr)) == nullptr) { + return false; + } + + // Unpack Transparent Container + sourceenb_to_targetenb_transparent_container_s container; + asn1::cbit_ref bref{msg.protocol_ies.source_to_target_transparent_container.value.data(), + msg.protocol_ies.source_to_target_transparent_container.value.size()}; + if (container.unpack(bref) != asn1::SRSASN_SUCCESS) { + s1ap_log->error("Failed to unpack SourceToTargetTransparentContainer\n"); + return false; + } + + // Handle Handover Resource Allocation + srslte::unique_byte_buffer_t ho_cmd = srslte::allocate_unique_buffer(*pool); + uint16_t rnti = rrc->start_ho_ue_resource_alloc(msg, container, *ho_cmd); + if (rnti == SRSLTE_INVALID_RNTI) { + return false; + } + + return send_ho_req_ack(msg, rnti, std::move(ho_cmd)); +} + +bool s1ap::send_ho_failure(uint32_t mme_ue_s1ap_id) +{ + s1ap_pdu_c tx_pdu; + tx_pdu.set_unsuccessful_outcome().load_info_obj(ASN1_S1AP_ID_HO_RES_ALLOC); + ho_fail_ies_container& container = tx_pdu.unsuccessful_outcome().value.ho_fail().protocol_ies; + + container.mme_ue_s1ap_id.value = mme_ue_s1ap_id; + // TODO: Setup cause + container.cause.value.set_radio_network().value = cause_radio_network_opts::ho_target_not_allowed; + + return sctp_send_s1ap_pdu(tx_pdu, SRSLTE_INVALID_RNTI, "HandoverFailure"); +} + +bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, uint16_t rnti, srslte::unique_byte_buffer_t ho_cmd) +{ + s1ap_pdu_c tx_pdu; + tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_HO_RES_ALLOC); + ho_request_ack_ies_container& container = tx_pdu.successful_outcome().value.ho_request_ack().protocol_ies; + + ue* ue_ptr = users.find_ue_mmeid(msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue_ptr->ctxt.rnti = rnti; + + container.mme_ue_s1ap_id.value = msg.protocol_ies.mme_ue_s1ap_id.value.value; + container.enb_ue_s1ap_id.value = ue_ptr->ctxt.enb_ue_s1ap_id; + + // TODO: Add admitted E-RABs + for (const auto& erab : msg.protocol_ies.erab_to_be_setup_list_ho_req.value) { + const erab_to_be_setup_item_ho_req_s& erabsetup = erab.value.erab_to_be_setup_item_ho_req(); + container.erab_admitted_list.value.push_back({}); + container.erab_admitted_list.value.back().load_info_obj(ASN1_S1AP_ID_ERAB_ADMITTED_ITEM); + auto& c = container.erab_admitted_list.value.back().value.erab_admitted_item(); + c.erab_id = erabsetup.erab_id; + c.gtp_teid = erabsetup.gtp_teid; + c.transport_layer_address = erabsetup.transport_layer_address; + } + asn1::s1ap::targetenb_to_sourceenb_transparent_container_s transparent_container; + transparent_container.rrc_container.resize(ho_cmd->N_bytes); + memcpy(transparent_container.rrc_container.data(), ho_cmd->msg, ho_cmd->N_bytes); + + auto& pdu = ho_cmd; // reuse pdu + asn1::bit_ref bref{pdu->msg, pdu->get_tailroom()}; + if (transparent_container.pack(bref) != asn1::SRSASN_SUCCESS) { + s1ap_log->error("Failed to pack TargeteNBToSourceeNBTransparentContainer\n"); + return false; + } + container.target_to_source_transparent_container.value.resize(bref.distance_bytes()); + memcpy(container.target_to_source_transparent_container.value.data(), pdu->msg, bref.distance_bytes()); + + return sctp_send_s1ap_pdu(tx_pdu, rnti, "HandoverRequestAcknowledge"); +} + /******************************************************************************* /* S1AP message senders ********************************************************************************/ @@ -1166,12 +1273,12 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt pcap->write_s1ap(buf->msg, buf->N_bytes); } - if (rnti > 0) { + if (rnti != SRSLTE_INVALID_RNTI) { s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending %s for rnti=0x%x", procedure_name, rnti); } else { s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending %s to MME", procedure_name); } - uint16_t streamid = rnti == 0 ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id; + uint16_t streamid = rnti == SRSLTE_INVALID_RNTI ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id; ssize_t n_sent = sctp_sendmsg(s1ap_socket.fd(), buf->msg, diff --git a/srsenb/test/common/dummy_classes.h b/srsenb/test/common/dummy_classes.h index e03ae47c1..19c334351 100644 --- a/srsenb/test/common/dummy_classes.h +++ b/srsenb/test/common/dummy_classes.h @@ -39,7 +39,7 @@ public: void phy_config_enabled(uint16_t rnti, bool enabled) override {} void write_mcch(asn1::rrc::sib_type2_s* sib2, asn1::rrc::sib_type13_r9_s* sib13, asn1::rrc::mcch_msg_s* mcch) override {} - uint16_t allocate_rnti() override { return last_rnti++; } + uint16_t reserve_new_crnti(const sched_interface::ue_cfg_t& ue_cfg) override { return last_rnti++; } uint16_t last_rnti = 70; };