setup of forwarding tunnels during enb s1 handover

master
Francisco 4 years ago committed by Francisco Paisana
parent ee3d8a5c77
commit 2befbd1825

@ -36,6 +36,8 @@ struct rrc_establishment_cause_opts;
struct cause_radio_network_opts;
struct bearers_subject_to_status_transfer_item_ies_o;
struct erab_level_qos_params_s;
struct ho_cmd_s;
struct erab_admitted_item_s;
template <class ies_set_paramT_>
struct protocol_ie_single_container_s;

@ -448,7 +448,10 @@ public:
* @param is_success true if ho cmd was received
* @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 void ho_preparation_complete(uint16_t rnti,
bool is_success,
const asn1::s1ap::ho_cmd_s& msg,
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) = 0;
@ -533,10 +536,10 @@ public:
/* Acknowledge Handover Request message back to MME.
* This message signals the completion of the HandoverPreparation from the TeNB point of view. */
virtual bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
srslte::unique_byte_buffer_t ho_cmd,
srslte::span<asn1::fixed_octstring<4, true> > admitted_bearers) = 0;
virtual bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
srslte::unique_byte_buffer_t ho_cmd,
srslte::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers) = 0;
/**
* Notify MME that Handover is complete

@ -203,7 +203,7 @@ public:
virtual void paging_completed(bool outcome) = 0;
virtual std::string get_rb_name(uint32_t lcid) = 0;
virtual uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) = 0;
virtual bool has_nr_dc() = 0;
virtual bool has_nr_dc() = 0;
};
// RRC interface for PDCP

@ -92,7 +92,10 @@ public:
std::vector<uint16_t>* erabs_released,
std::vector<uint16_t>* erabs_failed_to_release) 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 ho_preparation_complete(uint16_t rnti,
bool is_success,
const asn1::s1ap::ho_cmd_s& msg,
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) override;

@ -64,14 +64,20 @@ class bearer_cfg_handler
{
public:
struct erab_t {
struct gtpu_tunnel {
uint32_t teid_out = 0;
uint32_t teid_in = 0;
uint32_t addr = 0;
};
uint8_t id = 0;
asn1::s1ap::erab_level_qos_params_s qos_params;
asn1::bounded_bitstring<1, 160, true, true> address;
uint32_t teid_out = 0;
uint32_t teid_in = 0;
std::vector<gtpu_tunnel> tunnels;
};
bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_);
bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_, gtpu_interface_rrc* gtpu_);
int add_erab(uint8_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos,
@ -85,8 +91,12 @@ public:
const asn1::unbounded_octstring<true>* nas_pdu);
// Methods to apply bearer updates
void add_gtpu_bearer(gtpu_interface_rrc* gtpu, uint32_t erab_id);
void fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg);
void add_gtpu_bearer(uint32_t erab_id);
uint32_t add_gtpu_bearer(uint32_t erab_id,
uint32_t teid_out,
uint32_t addr,
const gtpu_interface_rrc::bearer_props* props = nullptr);
void fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg);
const std::map<uint8_t, erab_t>& get_erabs() const { return erabs; }
const asn1::rrc::drb_to_add_mod_list_l& get_established_drbs() const { return current_drbs; }
@ -98,6 +108,7 @@ private:
srslog::basic_logger& logger;
uint16_t rnti = 0;
const rrc_cfg_t* cfg = nullptr;
gtpu_interface_rrc* gtpu = nullptr;
// last cfg
asn1::rrc::drb_to_add_mod_list_l current_drbs;

@ -37,7 +37,9 @@ public:
bool fill_conn_recfg_no_ho_cmd(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg);
void handle_ue_meas_report(const asn1::rrc::meas_report_s& msg);
void handle_ho_preparation_complete(bool is_success, srslte::unique_byte_buffer_t container);
void handle_ho_preparation_complete(bool is_success,
const asn1::s1ap::ho_cmd_s& msg,
srslte::unique_byte_buffer_t container);
bool is_ho_running() const { return not is_in_state<idle_st>(); }
// S1-Handover
@ -56,7 +58,6 @@ private:
// Handover from source cell
bool start_ho_preparation(uint32_t target_eci, uint8_t measobj_id, bool fwd_direct_path_available);
bool start_enb_status_transfer();
// Handover to target cell
void fill_mobility_reconf_common(asn1::rrc::dl_dcch_msg_s& msg,
@ -99,32 +100,38 @@ private:
struct s1_target_ho_st {};
struct wait_recfg_comp {};
struct s1_source_ho_st : public subfsm_t<s1_source_ho_st> {
struct ho_cmd_msg {
const asn1::s1ap::ho_cmd_s* s1ap_ho_cmd;
const asn1::rrc::ho_cmd_r8_ies_s* ho_cmd;
};
ho_meas_report_ev report;
using ho_cmd_msg = asn1::rrc::ho_cmd_r8_ies_s;
struct wait_ho_req_ack_st {
struct wait_ho_cmd {
void enter(s1_source_ho_st* f, const ho_meas_report_ev& ev);
};
struct status_transfer_st {
void enter(s1_source_ho_st* f);
};
struct status_transfer_st {};
explicit s1_source_ho_st(rrc_mobility* parent_) : base_t(parent_) {}
explicit s1_source_ho_st(rrc_mobility* parent_);
private:
void send_ho_cmd(wait_ho_req_ack_st& s, const ho_cmd_msg& ho_cmd);
void handle_ho_cmd(wait_ho_cmd& s, const ho_cmd_msg& ho_cmd);
void handle_ho_cancel(const ho_cancel_ev& ev);
bool start_enb_status_transfer(const asn1::s1ap::ho_cmd_s& s1ap_ho_cmd);
rrc* rrc_enb;
rrc::ue* rrc_ue;
srslog::basic_logger& logger;
protected:
using fsm = s1_source_ho_st;
state_list<wait_ho_req_ack_st, status_transfer_st> states{this};
state_list<wait_ho_cmd, status_transfer_st> states{this};
// clang-format off
using transitions = transition_table<
// Start Target Event Action Guard
// +-------------------+------------------+---------------------+-----------------------+---------------------+
to_state< idle_st, srslte::failure_ev >,
to_state< idle_st, ho_cancel_ev, &fsm::handle_ho_cancel >,
row< wait_ho_req_ack_st, status_transfer_st, ho_cmd_msg, &fsm::send_ho_cmd >
row< wait_ho_cmd, status_transfer_st, ho_cmd_msg, &fsm::handle_ho_cmd >
// +-------------------+------------------+---------------------+-----------------------+---------------------+
>;
// clang-format on
@ -137,7 +144,7 @@ private:
// FSM transition handlers
void handle_crnti_ce(intraenb_ho_st& s, const user_crnti_upd_ev& ev);
void handle_recfg_complete(intraenb_ho_st& s, const recfg_complete_ev& ev);
void handle_ho_req(idle_st& s, const ho_req_rx_ev& ho_req);
void handle_ho_requested(idle_st& s, const ho_req_rx_ev& ho_req);
void handle_status_transfer(s1_target_ho_st& s, const status_transfer_ev& ev);
void defer_recfg_complete(s1_target_ho_st& s, const recfg_complete_ev& ev);
void handle_recfg_complete(wait_recfg_comp& s, const recfg_complete_ev& ev);
@ -159,7 +166,7 @@ protected:
// +----------------+-------------------+---------------------+----------------------------+-------------------------+
row< idle_st, s1_source_ho_st, ho_meas_report_ev, nullptr, &fsm::needs_s1_ho >,
row< idle_st, intraenb_ho_st, ho_meas_report_ev, nullptr, &fsm::needs_intraenb_ho >,
row< idle_st, s1_target_ho_st, ho_req_rx_ev, &fsm::handle_ho_req >,
row< idle_st, s1_target_ho_st, ho_req_rx_ev, &fsm::handle_ho_requested >,
// +----------------+-------------------+---------------------+----------------------------+-------------------------+
upd< intraenb_ho_st, user_crnti_upd_ev, &fsm::handle_crnti_ce >,
row< intraenb_ho_st, idle_st, recfg_complete_ev, &fsm::handle_recfg_complete >,

@ -76,9 +76,6 @@ public:
const asn1::s1ap::erab_level_qos_params_s& qos_params,
const asn1::unbounded_octstring<true>* nas_pdu);
// handover
void handle_ho_preparation_complete(bool is_success, srslte::unique_byte_buffer_t container);
void notify_s1ap_ue_ctxt_setup_complete();
void notify_s1ap_ue_erab_setup_response(const asn1::s1ap::erab_to_be_setup_list_bearer_su_req_l& e);

@ -75,10 +75,10 @@ public:
srslte::unique_byte_buffer_t rrc_container) override;
bool send_enb_status_transfer_proc(uint16_t rnti, std::vector<bearer_status_info>& 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,
srslte::span<asn1::fixed_octstring<4, true> > admitted_bearers) override;
bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
srslte::unique_byte_buffer_t ho_cmd,
srslte::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers) override;
void send_ho_notify(uint16_t rnti, uint64_t target_eci) override;
void send_ho_cancel(uint16_t rnti) override;
// void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps);
@ -174,6 +174,7 @@ private:
uint32_t target_eci = 0;
srslte::plmn_id_t target_plmn;
srslte::unique_byte_buffer_t rrc_container;
const asn1::s1ap::ho_cmd_s* ho_cmd_msg = nullptr;
};
explicit ue(s1ap* s1ap_ptr_);

@ -527,9 +527,12 @@ void rrc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size)
Handover functions
*******************************************************************************/
void rrc::ho_preparation_complete(uint16_t rnti, bool is_success, srslte::unique_byte_buffer_t rrc_container)
void rrc::ho_preparation_complete(uint16_t rnti,
bool is_success,
const asn1::s1ap::ho_cmd_s& msg,
srslte::unique_byte_buffer_t rrc_container)
{
users.at(rnti)->mobility_handler->handle_ho_preparation_complete(is_success, std::move(rrc_container));
users.at(rnti)->mobility_handler->handle_ho_preparation_complete(is_success, msg, std::move(rrc_container));
}
void rrc::set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs)

@ -200,8 +200,8 @@ void security_cfg_handler::regenerate_keys_handover(uint32_t new_pci, uint32_t n
* Bearer Handler
****************************/
bearer_cfg_handler::bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_) :
rnti(rnti_), cfg(&cfg_), logger(srslog::fetch_basic_logger("RRC"))
bearer_cfg_handler::bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_, gtpu_interface_rrc* gtpu_) :
rnti(rnti_), cfg(&cfg_), gtpu(gtpu_), logger(srslog::fetch_basic_logger("RRC"))
{}
int bearer_cfg_handler::add_erab(uint8_t erab_id,
@ -308,17 +308,35 @@ bool bearer_cfg_handler::modify_erab(uint8_t
return true;
}
void bearer_cfg_handler::add_gtpu_bearer(srsenb::gtpu_interface_rrc* gtpu, uint32_t erab_id)
void bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id)
{
auto it = erabs.find(erab_id);
if (it != erabs.end()) {
erab_t& erab = it->second;
// Initialize ERAB in GTPU right-away. DRBs are only created during RRC setup/reconf
uint32_t addr_ = erab.address.to_number();
erab.teid_in = gtpu->add_bearer(rnti, erab.id - 2, addr_, erab.teid_out);
} else {
if (it == erabs.end()) {
logger.error("Adding erab_id=%d to GTPU", erab_id);
return;
}
it->second.teid_in = add_gtpu_bearer(erab_id, it->second.teid_out, it->second.address.to_number(), nullptr);
}
uint32_t bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id,
uint32_t teid_out,
uint32_t addr,
const gtpu_interface_rrc::bearer_props* props)
{
auto it = erabs.find(erab_id);
if (it == erabs.end()) {
logger.error("Adding erab_id=%d to GTPU", erab_id);
return 0;
}
// Initialize ERAB tunnel in GTPU right-away. DRBs are only created during RRC setup/reconf
erab_t& erab = it->second;
erab_t::gtpu_tunnel bearer;
bearer.teid_out = teid_out;
bearer.addr = addr;
bearer.teid_in = gtpu->add_bearer(rnti, erab.id - 2, addr, teid_out, props);
erab.tunnels.push_back(bearer);
return bearer.teid_in;
}
void bearer_cfg_handler::fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg)

@ -380,7 +380,9 @@ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci,
* @param is_success flag to whether an HandoverCommand or HandoverReject was received
* @param container RRC container with HandoverCommand to send to UE
*/
void rrc::ue::rrc_mobility::handle_ho_preparation_complete(bool is_success, srslte::unique_byte_buffer_t container)
void rrc::ue::rrc_mobility::handle_ho_preparation_complete(bool is_success,
const asn1::s1ap::ho_cmd_s& msg,
srslte::unique_byte_buffer_t container)
{
if (not is_success) {
log_h->info("Received S1AP HandoverFailure. Aborting Handover...");
@ -405,7 +407,7 @@ void rrc::ue::rrc_mobility::handle_ho_preparation_complete(bool is_success, srsl
return;
}
trigger(rrchocmd.crit_exts.c1().ho_cmd_r8());
trigger(s1_source_ho_st::ho_cmd_msg{&msg, &rrchocmd.crit_exts.c1().ho_cmd_r8()});
}
bool rrc::ue::rrc_mobility::start_s1_tenb_ho(
@ -476,12 +478,46 @@ void rrc::ue::rrc_mobility::fill_mobility_reconf_common(asn1::rrc::dl_dcch_msg_s
true);
}
/*************************************
* rrc_mobility FSM methods
*************************************/
bool rrc::ue::rrc_mobility::needs_s1_ho(idle_st& s, const ho_meas_report_ev& meas_result)
{
if (rrc_ue->get_state() != RRC_STATE_REGISTERED) {
return false;
}
return rrc_details::eci_to_enbid(meas_result.target_eci) != rrc_enb->cfg.enb_id;
}
bool rrc::ue::rrc_mobility::needs_intraenb_ho(idle_st& s, const ho_meas_report_ev& meas_result)
{
if (rrc_ue->get_state() != RRC_STATE_REGISTERED) {
return false;
}
if (rrc_details::eci_to_enbid(meas_result.target_eci) != rrc_enb->cfg.enb_id) {
return false;
}
uint32_t cell_id = rrc_details::eci_to_cellid(meas_result.target_eci);
return rrc_ue->get_ue_cc_cfg(UE_PCELL_CC_IDX)->cell_cfg.cell_id != cell_id;
}
/*************************************
* s1_source_ho subFSM methods
*************************************/
rrc::ue::rrc_mobility::s1_source_ho_st::s1_source_ho_st(rrc_mobility* parent_) :
base_t(parent_), rrc_enb(parent_->rrc_enb), rrc_ue(parent_->rrc_ue), logger(parent_->logger)
{}
/**
* TS 36.413, Section 8.4.6 - eNB Status Transfer
* Description: Send "eNBStatusTransfer" message from source eNB to MME
* - Pass bearers' DL/UL HFN and PDCP SN to be put inside a transparent container
* @brief: Send "eNBStatusTransfer" message from source eNB to MME, and setup Forwarding GTPU tunnel
* - PDCP provides the bearers' DL/UL HFN and COUNT to be put inside a transparent container
* - The eNB sends eNBStatusTransfer to MME
* - A GTPU forwarding tunnel is opened to forward buffered PDCP PDUs and incoming GTPU PDUs
*/
bool rrc::ue::rrc_mobility::start_enb_status_transfer()
bool rrc::ue::rrc_mobility::s1_source_ho_st::start_enb_status_transfer(const asn1::s1ap::ho_cmd_s& s1ap_ho_cmd)
{
std::vector<s1ap_interface_rrc::bearer_status_info> s1ap_bearers;
s1ap_bearers.reserve(rrc_ue->bearer_list.get_erabs().size());
@ -503,42 +539,41 @@ bool rrc::ue::rrc_mobility::start_enb_status_transfer()
}
Info("PDCP Bearer list sent to S1AP to initiate the eNB Status Transfer");
return rrc_enb->s1ap->send_enb_status_transfer_proc(rrc_ue->rnti, s1ap_bearers);
}
/*************************************
* rrc_mobility FSM methods
*************************************/
bool rrc::ue::rrc_mobility::needs_s1_ho(idle_st& s, const ho_meas_report_ev& meas_result)
{
if (rrc_ue->get_state() != RRC_STATE_REGISTERED) {
if (not rrc_enb->s1ap->send_enb_status_transfer_proc(rrc_ue->rnti, s1ap_bearers)) {
return false;
}
return rrc_details::eci_to_enbid(meas_result.target_eci) != rrc_enb->cfg.enb_id;
}
bool rrc::ue::rrc_mobility::needs_intraenb_ho(idle_st& s, const ho_meas_report_ev& meas_result)
{
if (rrc_ue->get_state() != RRC_STATE_REGISTERED) {
return false;
}
if (rrc_details::eci_to_enbid(meas_result.target_eci) != rrc_enb->cfg.enb_id) {
return false;
// Setup GTPU forwarding tunnel
if (s1ap_ho_cmd.protocol_ies.erab_subjectto_data_forwarding_list_present) {
const auto& fwd_erab_list = s1ap_ho_cmd.protocol_ies.erab_subjectto_data_forwarding_list.value;
const auto& erab_list = rrc_ue->bearer_list.get_erabs();
for (const auto& e : fwd_erab_list) {
const auto& fwd_erab = e.value.erab_data_forwarding_item();
auto it = erab_list.find(fwd_erab.erab_id);
if (it == erab_list.end()) {
Warning("E-RAB id=%d subject to forwarding not found\n", fwd_erab.erab_id);
continue;
}
const bearer_cfg_handler::erab_t& erab = it->second;
if (fwd_erab.dl_g_tp_teid_present and fwd_erab.dl_transport_layer_address_present) {
gtpu_interface_rrc::bearer_props props;
props.forward_from_teidin_present = true;
props.forward_from_teidin = erab.teid_in;
rrc_ue->bearer_list.add_gtpu_bearer(fwd_erab.erab_id,
fwd_erab.dl_g_tp_teid.to_number(),
fwd_erab.dl_transport_layer_address.to_number(),
&props);
}
}
}
uint32_t cell_id = rrc_details::eci_to_cellid(meas_result.target_eci);
return rrc_ue->get_ue_cc_cfg(UE_PCELL_CC_IDX)->cell_cfg.cell_id != cell_id;
}
/*************************************
* s1_source_ho subFSM methods
*************************************/
return true;
}
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)
void rrc::ue::rrc_mobility::s1_source_ho_st::wait_ho_cmd::enter(s1_source_ho_st* f, const ho_meas_report_ev& ev)
{
srslte::console("Starting S1 Handover of rnti=0x%x to cellid=0x%x.\n", f->parent_fsm()->rrc_ue->rnti, ev.target_eci);
f->get_log()->info(
"Starting S1 Handover of rnti=0x%x to cellid=0x%x.", f->parent_fsm()->rrc_ue->rnti, ev.target_eci);
srslte::console("Starting S1 Handover of rnti=0x%x to cellid=0x%x.\n", f->rrc_ue->rnti, ev.target_eci);
f->get_log()->info("Starting S1 Handover of rnti=0x%x to cellid=0x%x.", f->rrc_ue->rnti, ev.target_eci);
f->report = ev;
bool success = f->parent_fsm()->start_ho_preparation(f->report.target_eci, f->report.meas_obj->meas_obj_id, false);
@ -547,65 +582,74 @@ void rrc::ue::rrc_mobility::s1_source_ho_st::wait_ho_req_ack_st::enter(s1_source
}
}
void rrc::ue::rrc_mobility::s1_source_ho_st::send_ho_cmd(wait_ho_req_ack_st& s, const ho_cmd_r8_ies_s& ho_cmd)
/**
* TS 36.413, Section 8.4.2 - Handover Resource Allocation
* @brief: Send "eNBStatusTransfer" message from source eNB to MME, and setup Forwarding GTPU tunnel
* - PDCP provides the bearers' DL/UL HFN and COUNT to be put inside a transparent container
* - The eNB sends eNBStatusTransfer to MME
* - A GTPU forwarding tunnel is opened to forward buffered PDCP PDUs and incoming GTPU PDUs
*/
void rrc::ue::rrc_mobility::s1_source_ho_st::handle_ho_cmd(wait_ho_cmd& s, const ho_cmd_msg& ho_cmd)
{
/* unpack DL-DCCH message containing the RRCRonnectionReconf (with MobilityInfo) to be sent to the UE */
asn1::rrc::dl_dcch_msg_s dl_dcch_msg;
{
asn1::cbit_ref bref(&ho_cmd.ho_cmd_msg[0], ho_cmd.ho_cmd_msg.size());
asn1::cbit_ref bref(&ho_cmd.ho_cmd->ho_cmd_msg[0], ho_cmd.ho_cmd->ho_cmd_msg.size());
if (dl_dcch_msg.unpack(bref) != asn1::SRSASN_SUCCESS) {
get_log()->warning("Unpacking of RRC DL-DCCH message with HO Command was unsuccessful.");
Warning("Unpacking of RRC DL-DCCH message with HO Command was unsuccessful.");
trigger(ho_cancel_ev{});
return;
}
}
if (dl_dcch_msg.msg.type().value != dl_dcch_msg_type_c::types_opts::c1 or
dl_dcch_msg.msg.c1().type().value != dl_dcch_msg_type_c::c1_c_::types_opts::rrc_conn_recfg) {
get_log()->warning("HandoverCommand is expected to contain an RRC Connection Reconf message inside");
Warning("HandoverCommand is expected to contain an RRC Connection Reconf message inside");
trigger(ho_cancel_ev{});
return;
}
asn1::rrc::rrc_conn_recfg_s& reconf = dl_dcch_msg.msg.c1().rrc_conn_recfg();
if (not reconf.crit_exts.c1().rrc_conn_recfg_r8().mob_ctrl_info_present) {
get_log()->warning("HandoverCommand is expected to have mobility control subfield");
Warning("HandoverCommand is expected to have mobility control subfield");
trigger(ho_cancel_ev{});
return;
}
// Disable DRBs
parent_fsm()->rrc_ue->mac_ctrl.set_drb_activation(false);
parent_fsm()->rrc_ue->mac_ctrl.update_mac(mac_controller::proc_stage_t::other);
/* Enter Handover Execution */
// TODO: Do anything with MeasCfg info within the Msg (e.g. update ue_var_meas)?
// Disable DRBs in the MAC, while Reconfiguration is taking place.
rrc_ue->mac_ctrl.set_drb_activation(false);
rrc_ue->mac_ctrl.update_mac(mac_controller::proc_stage_t::other);
/* Send HO Command to UE */
if (not parent_fsm()->rrc_ue->send_dl_dcch(&dl_dcch_msg)) {
// Send HO Command to UE
if (not rrc_ue->send_dl_dcch(&dl_dcch_msg)) {
trigger(ho_cancel_ev{});
return;
}
/* Start S1AP eNBStatusTransfer Procedure */
if (not start_enb_status_transfer(*ho_cmd.s1ap_ho_cmd)) {
trigger(ho_cancel_ev{});
}
}
//! Called in Source ENB during S1-Handover when there was a Reestablishment Request
void rrc::ue::rrc_mobility::s1_source_ho_st::handle_ho_cancel(const ho_cancel_ev& ev)
{
parent_fsm()->rrc_enb->s1ap->send_ho_cancel(parent_fsm()->rrc_ue->rnti);
}
void rrc::ue::rrc_mobility::s1_source_ho_st::status_transfer_st::enter(s1_source_ho_st* f)
{
f->get_log()->info("HandoverCommand of rnti=0x%x handled successfully.", f->parent_fsm()->rrc_ue->rnti);
// TODO: Do anything with MeasCfg info within the Msg (e.g. update ue_var_meas)?
/* Start S1AP eNBStatusTransfer Procedure */
if (not f->parent_fsm()->start_enb_status_transfer()) {
f->trigger(srslte::failure_ev{});
}
rrc_enb->s1ap->send_ho_cancel(rrc_ue->rnti);
}
/*************************************
* s1_target_ho state methods
*************************************/
void rrc::ue::rrc_mobility::handle_ho_req(idle_st& s, const ho_req_rx_ev& ho_req)
/**
* @brief handle S1AP "HO Requested" message from the MME
* - MME --> TeNB
* @param s initial state
* @param ho_req event with received message
*/
void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& ho_req)
{
const auto& rrc_container = ho_req.transparent_container->rrc_container;
@ -685,12 +729,42 @@ void rrc::ue::rrc_mobility::handle_ho_req(idle_st& s, const ho_req_rx_ev& ho_req
// Apply PHY updates
rrc_ue->apply_reconf_phy_config(recfg_r8, true);
/* send S1AP HandoverRequestAcknowledge */
std::vector<asn1::fixed_octstring<4, true> > admitted_erabs;
// Set admitted E-RABs
std::vector<asn1::s1ap::erab_admitted_item_s> admitted_erabs;
for (auto& erab : rrc_ue->bearer_list.get_erabs()) {
admitted_erabs.emplace_back();
srslte::uint32_to_uint8(erab.second.teid_in, admitted_erabs.back().data());
asn1::s1ap::erab_admitted_item_s& admitted_erab = admitted_erabs.back();
admitted_erab.erab_id = erab.second.id;
srslte::uint32_to_uint8(erab.second.teid_in, admitted_erab.gtp_teid.data());
// Establish GTPU Forwarding Paths
if (ho_req.transparent_container->erab_info_list_present) {
auto& lst = ho_req.transparent_container->erab_info_list;
auto it = std::find_if(
lst.begin(),
lst.end(),
[&erab](const asn1::s1ap::protocol_ie_single_container_s<asn1::s1ap::erab_info_list_ies_o>& fwd_erab) {
return fwd_erab.value.erab_info_list_item().erab_id == erab.second.id;
});
if (it == lst.end()) {
continue;
}
auto& fwd_erab = it->value.erab_info_list_item();
if (fwd_erab.dl_forwarding_present and
fwd_erab.dl_forwarding.value == asn1::s1ap::dl_forwarding_opts::dl_forwarding_proposed) {
admitted_erab.dl_g_tp_teid_present = true;
gtpu_interface_rrc::bearer_props props;
props.flush_before_teidin_present = true;
props.flush_before_teidin = erab.second.teid_in;
uint32_t dl_teid_in = rrc_ue->bearer_list.add_gtpu_bearer(
erab.second.id, erab.second.teid_out, erab.second.address.to_number(), &props);
srslte::uint32_to_uint8(dl_teid_in, admitted_erabs.back().dl_g_tp_teid.data());
}
}
}
// send S1AP HandoverRequestAcknowledge
if (not rrc_enb->s1ap->send_ho_req_ack(*ho_req.ho_req_msg, rrc_ue->rnti, std::move(ho_cmd_pdu), admitted_erabs)) {
trigger(srslte::failure_ev{});
return;
@ -721,11 +795,12 @@ bool rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& ho
get_log()->warning("Data Forwarding of E-RABs not supported");
}
// Create E-RAB and associated main GTPU tunnel
uint32_t teid_out;
srslte::uint8_to_uint32(erab.gtp_teid.data(), &teid_out);
rrc_ue->bearer_list.add_erab(
erab.erab_id, erab.erab_level_qos_params, erab.transport_layer_address, teid_out, nullptr);
rrc_ue->bearer_list.add_gtpu_bearer(rrc_enb->gtpu, erab.erab_id);
rrc_ue->bearer_list.add_gtpu_bearer(erab.erab_id);
}
// Regenerate AS Keys
@ -900,8 +975,7 @@ void rrc::ue::rrc_mobility::handle_crnti_ce(intraenb_ho_st& s, const user_crnti_
void rrc::ue::rrc_mobility::handle_recfg_complete(intraenb_ho_st& s, const recfg_complete_ev& ev)
{
logger.info(
"User rnti=0x%x successfully handovered to cell_id=0x%x", rrc_ue->rnti, s.target_cell->cell_cfg.cell_id);
logger.info("User rnti=0x%x successfully handovered to cell_id=0x%x", rrc_ue->rnti, s.target_cell->cell_cfg.cell_id);
}
} // namespace srsenb

@ -36,7 +36,7 @@ rrc::ue::ue(rrc* outer_rrc, uint16_t rnti_, const sched_interface::ue_cfg_t& sch
pool(srslte::byte_buffer_pool::get_instance()),
phy_rrc_dedicated_list(sched_ue_cfg.supported_cc_list.size()),
ue_cell_list(parent->cfg, *outer_rrc->cell_res_list, *outer_rrc->cell_common_list),
bearer_list(rnti_, parent->cfg),
bearer_list(rnti_, parent->cfg, outer_rrc->gtpu),
ue_security_cfg(parent->cfg),
mac_ctrl(rnti, ue_cell_list, bearer_list, parent->cfg, parent->mac, *parent->cell_common_list, sched_ue_cfg)
{}
@ -858,7 +858,7 @@ bool rrc::ue::setup_erabs(const asn1::s1ap::erab_to_be_setup_list_ctxt_su_req_l&
srslte::uint8_to_uint32(erab.gtp_teid.data(), &teid_out);
const asn1::unbounded_octstring<true>* nas_pdu = erab.nas_pdu_present ? &erab.nas_pdu : nullptr;
bearer_list.add_erab(erab.erab_id, erab.erab_level_qos_params, erab.transport_layer_address, teid_out, nas_pdu);
bearer_list.add_gtpu_bearer(parent->gtpu, erab.erab_id);
bearer_list.add_gtpu_bearer(erab.erab_id);
}
return true;
}
@ -882,7 +882,7 @@ bool rrc::ue::setup_erabs(const asn1::s1ap::erab_to_be_setup_list_bearer_su_req_
srslte::uint8_to_uint32(erab.gtp_teid.data(), &teid_out);
bearer_list.add_erab(
erab.erab_id, erab.erab_level_qos_params, erab.transport_layer_address, teid_out, &erab.nas_pdu);
bearer_list.add_gtpu_bearer(parent->gtpu, erab.erab_id);
bearer_list.add_gtpu_bearer(erab.erab_id);
}
// Work in progress
@ -978,13 +978,6 @@ void rrc::ue::update_scells()
parent->logger.info("SCells activated for rnti=0x%x", rnti);
}
/********************** Handover **************************/
void rrc::ue::handle_ho_preparation_complete(bool is_success, srslte::unique_byte_buffer_t container)
{
mobility_handler->handle_ho_preparation_complete(is_success, std::move(container));
}
/********************** HELPERS ***************************/
void rrc::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg)

@ -59,6 +59,7 @@ srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::init(uint32_t
srslte::plmn_id_t target_plmn_,
srslte::unique_byte_buffer_t rrc_container_)
{
ho_cmd_msg = nullptr;
target_eci = target_eci_;
target_plmn = target_plmn_;
@ -113,12 +114,6 @@ srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(const asn1::s1ap::ho_cmd_
// TODO
}
// Check for E-RABs subject to being forwarded
if (msg.protocol_ies.erab_subjectto_data_forwarding_list_present) {
procWarning("Not handling E-RABSubjecttoDataForwardingList");
// TODO
}
// In case of intra-system Handover, Target to Source Transparent Container IE shall be encoded as
// Target eNB to Source eNB Transparent Container IE
asn1::cbit_ref bref(msg.protocol_ies.target_to_source_transparent_container.value.data(),
@ -140,6 +135,7 @@ srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(const asn1::s1ap::ho_cmd_
}
memcpy(rrc_container->msg, container.rrc_container.data(), container.rrc_container.size());
rrc_container->N_bytes = container.rrc_container.size();
ho_cmd_msg = &msg;
return srslte::proc_outcome_t::success;
}
@ -147,9 +143,9 @@ srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(const asn1::s1ap::ho_cmd_
void s1ap::ue::ho_prep_proc_t::then(const srslte::proc_state_t& result)
{
if (result.is_error()) {
s1ap_ptr->rrc->ho_preparation_complete(ue_ptr->ctxt.rnti, false, {});
s1ap_ptr->rrc->ho_preparation_complete(ue_ptr->ctxt.rnti, false, *ho_cmd_msg, {});
} else {
s1ap_ptr->rrc->ho_preparation_complete(ue_ptr->ctxt.rnti, true, std::move(rrc_container));
s1ap_ptr->rrc->ho_preparation_complete(ue_ptr->ctxt.rnti, true, *ho_cmd_msg, std::move(rrc_container));
procInfo("Completed with success");
}
}
@ -936,10 +932,10 @@ bool s1ap::send_ho_failure(uint32_t mme_ue_s1ap_id)
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,
srslte::span<asn1::fixed_octstring<4, true> > admitted_bearers)
bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
srslte::unique_byte_buffer_t ho_cmd,
srslte::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers)
{
s1ap_pdu_c tx_pdu;
tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_HO_RES_ALLOC);
@ -951,21 +947,26 @@ bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
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 (uint32_t i = 0; i < msg.protocol_ies.erab_to_be_setup_list_ho_req.value.size(); ++i) {
const auto& erab = msg.protocol_ies.erab_to_be_setup_list_ho_req.value[i];
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 = admitted_bearers[i];
// Add admitted E-RABs
container.erab_admitted_list.value.resize(admitted_bearers.size());
for (size_t i = 0; i < admitted_bearers.size(); ++i) {
container.erab_admitted_list.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_ADMITTED_ITEM);
auto& c = container.erab_admitted_list.value[i].value.erab_admitted_item();
c = admitted_bearers[i];
c.transport_layer_address = addr_to_asn1(args.gtp_bind_addr.c_str());
// c.dl_transport_layer_address_present = true;
// c.dl_transport_layer_address = c.transport_layer_address;
// c.ul_transport_layer_address_present = true;
// c.ul_transport_layer_address = c.transport_layer_address;
// If E-RAB is proposed for forward tunneling
if (c.dl_g_tp_teid_present) {
c.dl_transport_layer_address_present = true;
c.dl_transport_layer_address = c.transport_layer_address;
}
if (c.ul_gtp_teid_present) {
c.ul_transport_layer_address_present = true;
c.ul_transport_layer_address = c.transport_layer_address;
}
}
// Pack transparent container
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);
@ -1636,8 +1637,16 @@ bool s1ap::ue::send_ho_required(uint32_t target_eci,
/*** fill the transparent container ***/
container.source_to_target_transparent_container_secondary_present = false;
sourceenb_to_targetenb_transparent_container_s transparent_cntr;
transparent_cntr.erab_info_list_present = false; // TODO: CHECK
transparent_cntr.erab_info_list_present = true; // TODO: CHECK
transparent_cntr.subscriber_profile_idfor_rfp_present = false; // TODO: CHECK
// FIXME: Hardcoded ERABs
transparent_cntr.erab_info_list.resize(1);
transparent_cntr.erab_info_list[0].load_info_obj(ASN1_S1AP_ID_ERAB_INFO_LIST_ITEM);
transparent_cntr.erab_info_list[0].value.erab_info_list_item().erab_id = 5;
transparent_cntr.erab_info_list[0].value.erab_info_list_item().dl_forwarding_present = true;
transparent_cntr.erab_info_list[0].value.erab_info_list_item().dl_forwarding.value =
dl_forwarding_opts::dl_forwarding_proposed;
// - set target cell ID
target_plmn.to_s1ap_plmn_bytes(transparent_cntr.target_cell_id.plm_nid.data());
transparent_cntr.target_cell_id.cell_id.from_number(target_eci); // [ENBID|CELLID|0]

@ -105,10 +105,10 @@ public:
{
return true;
}
bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
srslte::unique_byte_buffer_t ho_cmd,
srslte::span<asn1::fixed_octstring<4, true> > admitted_bearers) override
bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
srslte::unique_byte_buffer_t ho_cmd,
srslte::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers) override
{
return true;
}

@ -226,7 +226,7 @@ int test_s1ap_mobility(srslte::log_sink_spy& spy, mobility_test_params test_para
/* Test Case: HandoverPreparation has failed */
if (test_params.fail_at == mobility_test_params::test_event::ho_prep_failure) {
tester.rrc.ho_preparation_complete(tester.rnti, false, nullptr);
tester.rrc.ho_preparation_complete(tester.rnti, false, {}, nullptr);
// TESTASSERT(spy.get_error_counter() == 1);
TESTASSERT(not s1ap.last_enb_status.status_present);
return SRSLTE_SUCCESS;
@ -239,7 +239,7 @@ int test_s1ap_mobility(srslte::log_sink_spy& spy, mobility_test_params test_para
0x86, 0x0d, 0x30, 0x00, 0x0b, 0x5a, 0x02, 0x17, 0x86, 0x00, 0x05, 0xa0, 0x20};
test_helpers::copy_msg_to_buffer(pdu, ho_cmd_rrc_container);
TESTASSERT(s1ap.last_enb_status.rnti != tester.rnti);
tester.rrc.ho_preparation_complete(tester.rnti, true, std::move(pdu));
tester.rrc.ho_preparation_complete(tester.rnti, true, asn1::s1ap::ho_cmd_s{}, std::move(pdu));
TESTASSERT(s1ap.last_enb_status.status_present);
TESTASSERT(spy.get_error_counter() == 0);
asn1::rrc::dl_dcch_msg_s ho_cmd;
@ -282,6 +282,13 @@ int test_s1ap_tenb_mobility(mobility_test_params test_params)
0x5c, 0xe1, 0x86, 0x35, 0x39, 0x80, 0x0e, 0x06, 0xa4, 0x40, 0x0f, 0x22, 0x78};
// 0a100b818000018000f3020800001580001406a402f00404f00014804a000000021231b6f83ea06f05e465141d39d0544c00025400200460000000100100c000000000020500041400670dfbc46606500f00080020800c14ca2d5ce1863539800e06a4400f2278
container.rrc_container.resize(sizeof(ho_prep_container));
container.erab_info_list_present = true;
container.erab_info_list.resize(1);
container.erab_info_list[0].load_info_obj(ASN1_S1AP_ID_ERAB_INFO_LIST_ITEM);
container.erab_info_list[0].value.erab_info_list_item().erab_id = 5;
container.erab_info_list[0].value.erab_info_list_item().dl_forwarding_present = true;
container.erab_info_list[0].value.erab_info_list_item().dl_forwarding.value =
asn1::s1ap::dl_forwarding_opts::dl_forwarding_proposed;
memcpy(container.rrc_container.data(), ho_prep_container, sizeof(ho_prep_container));
tester.rrc.start_ho_ue_resource_alloc(ho_req, container);
tester.tic();

@ -75,9 +75,9 @@ public:
} last_enb_status = {};
std::vector<uint8_t> added_erab_ids;
struct ho_req_ack {
uint16_t rnti;
srslte::unique_byte_buffer_t ho_cmd_pdu;
std::vector<asn1::fixed_octstring<4, true> > admitted_bearers;
uint16_t rnti;
srslte::unique_byte_buffer_t ho_cmd_pdu;
std::vector<asn1::s1ap::erab_admitted_item_s> admitted_bearers;
} last_ho_req_ack;
bool send_ho_required(uint16_t rnti,
@ -93,10 +93,10 @@ public:
last_enb_status = {true, rnti, bearer_status_list};
return true;
}
bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
srslte::unique_byte_buffer_t ho_cmd,
srslte::span<asn1::fixed_octstring<4, true> > admitted_bearers) override
bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
srslte::unique_byte_buffer_t ho_cmd,
srslte::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers) override
{
last_ho_req_ack.rnti = rnti;
last_ho_req_ack.ho_cmd_pdu = std::move(ho_cmd);

Loading…
Cancel
Save