handover - setup s1ap cause in case of handover failure at the target enb

master
Francisco 4 years ago committed by Francisco Paisana
parent 49bd895e29
commit d2c404b166

@ -39,6 +39,8 @@ struct erab_level_qos_params_s;
struct ho_cmd_s;
struct erab_admitted_item_s;
struct erab_to_be_modified_item_bearer_mod_req_s;
struct cause_c;
struct erab_item_s;
template <class ies_set_paramT_>
struct protocol_ie_single_container_s;

@ -52,7 +52,8 @@ public:
srsran::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;
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container,
asn1::s1ap::cause_c& failure_cause) = 0;
virtual void set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs) = 0;
};

@ -86,25 +86,23 @@ public:
*/
virtual bool send_enb_status_transfer_proc(uint16_t rnti, std::vector<bearer_status_info>& bearer_status_list) = 0;
/* 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,
uint32_t enb_cc_idx,
srsran::unique_byte_buffer_t ho_cmd,
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers) = 0;
/**
* Notify MME that Handover is complete
*/
virtual void send_ho_notify(uint16_t rnti, uint64_t target_eci) = 0;
/**
* Cancel on-going S1 Handover. MME should release UE context in target eNB
* SeNB --> MME
*/
virtual void send_ho_cancel(uint16_t rnti) = 0;
/*************************
* Target eNB Handover
************************/
virtual bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
uint32_t enb_cc_idx,
srsran::unique_byte_buffer_t ho_cmd,
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers,
srsran::const_span<asn1::s1ap::erab_item_s> not_admitted_bearers) = 0;
virtual void send_ho_notify(uint16_t rnti, uint64_t target_eci) = 0;
/**
* Called during release of a subset of eNB E-RABs. Send E-RAB RELEASE INDICATION to MME.
* SeNB --> MME

@ -14493,6 +14493,7 @@ std::string erab_modify_resp_ies_o::value_c::types_opts::to_string() const
}
template struct asn1::s1ap::protocol_ie_field_s<erab_modify_resp_ies_o>;
template struct asn1::s1ap::protocol_ie_single_container_s<asn1::s1ap::erab_failedto_setup_item_ho_req_ack_ies_o>;
erab_modify_resp_ies_container::erab_modify_resp_ies_container() :
mme_ue_s1ap_id(0, crit_e::ignore),

@ -101,7 +101,8 @@ public:
srsran::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;
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container,
asn1::s1ap::cause_c& failure_cause) override;
void set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs) override;
// rrc_interface_pdcp

@ -91,7 +91,7 @@ public:
const asn1::unbounded_octstring<true>* nas_pdu);
// Methods to apply bearer updates
void add_gtpu_bearer(uint32_t erab_id);
int add_gtpu_bearer(uint32_t erab_id);
srsran::expected<uint32_t> add_gtpu_bearer(uint32_t erab_id,
uint32_t teid_out,
uint32_t addr,

@ -45,10 +45,6 @@ public:
bool start_s1_tenb_ho(const asn1::s1ap::ho_request_s& msg,
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container);
static uint16_t
start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg,
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container);
private:
// helper methods
bool update_ue_var_meas_cfg(uint32_t src_earfcn,
@ -63,7 +59,9 @@ private:
const enb_cell_common& target_cell,
uint32_t src_dl_earfcn,
uint32_t src_pci);
bool apply_ho_prep_cfg(const asn1::rrc::ho_prep_info_r8_ies_s& ho_prep, const asn1::s1ap::ho_request_s& ho_req_msg);
void apply_ho_prep_cfg(const asn1::rrc::ho_prep_info_r8_ies_s& ho_prep,
const asn1::s1ap::ho_request_s& ho_req_msg,
std::vector<asn1::s1ap::erab_item_s>& erabs_failed_to_setup);
rrc::ue* rrc_ue = nullptr;
rrc* rrc_enb = nullptr;
@ -82,7 +80,10 @@ private:
const asn1::s1ap::ho_request_s* ho_req_msg;
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s* transparent_container;
};
using unsuccessful_outcome_ev = std::false_type;
struct ho_failure_ev {
asn1::s1ap::cause_c cause;
ho_failure_ev(const asn1::s1ap::cause_c& cause_) : cause(cause_) {}
};
using recfg_complete_ev = asn1::rrc::rrc_conn_recfg_complete_s;
using status_transfer_ev = asn1::s1ap::bearers_subject_to_status_transfer_list_l;
@ -96,6 +97,7 @@ private:
void enter(rrc_mobility* f, const ho_meas_report_ev& meas_report);
};
struct s1_target_ho_st {
asn1::s1ap::cause_c failure_cause;
std::vector<uint32_t> pending_tunnels;
};
struct wait_recfg_comp {};
@ -145,6 +147,7 @@ private:
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_requested(idle_st& s, const ho_req_rx_ev& ho_req);
void handle_ho_failure(s1_target_ho_st& s, const ho_failure_ev& ev);
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);
@ -172,6 +175,7 @@ protected:
row< intraenb_ho_st, idle_st, recfg_complete_ev, &fsm::handle_recfg_complete >,
// +----------------+-------------------+---------------------+----------------------------+-------------------------+
row< s1_target_ho_st, wait_recfg_comp, status_transfer_ev, &fsm::handle_status_transfer >,
row< s1_target_ho_st, idle_st, ho_failure_ev, &fsm::handle_ho_failure >,
upd< s1_target_ho_st, recfg_complete_ev, &fsm::defer_recfg_complete >,
row< wait_recfg_comp, idle_st, recfg_complete_ev, &fsm::handle_recfg_complete >
// +----------------+-------------------+---------------------+----------------------------+-------------------------+

@ -84,13 +84,12 @@ public:
srsran::span<uint32_t> fwd_erabs,
srsran::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,
uint32_t enb_cc_idx,
srsran::unique_byte_buffer_t ho_cmd,
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers) override;
void send_ho_notify(uint16_t rnti, uint64_t target_eci) override;
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers,
srsran::const_span<asn1::s1ap::erab_item_s> not_admitted_bearers) override;
void send_ho_cancel(uint16_t rnti) override;
bool release_erabs(uint16_t rnti, const std::vector<uint16_t>& erabs_successfully_released) override;
bool send_error_indication(const asn1::s1ap::cause_c& cause,
@ -98,6 +97,12 @@ public:
srsran::optional<uint32_t> mme_ue_s1ap_id = {});
bool send_ue_cap_info_indication(uint16_t rnti, srsran::unique_byte_buffer_t ue_radio_cap) override;
/// Target eNB Handover
/// Section 8.4.2 - Handover Resource Allocation
void send_ho_failure(uint32_t mme_ue_s1ap_id, const asn1::s1ap::cause_c& cause);
/// Section 8.4.3 - Handover Notification
void send_ho_notify(uint16_t rnti, uint64_t target_eci) override;
// Stack interface
bool
handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags);

@ -222,8 +222,8 @@ int bearer_cfg_handler::add_erab(uint8_t
logger->error("QCI=%d not configured", qos.qci);
return SRSRAN_ERROR;
}
if (lcid < 3 or lcid > 10) {
logger->error("DRB logical channel ids must be within 3 and 10");
if (not srsran::is_lte_drb(lcid)) {
logger->error("E-RAB=%d logical channel id=%d is invalid", erab_id, lcid);
return SRSRAN_ERROR;
}
const rrc_cfg_qci_t& qci_cfg = qci_it->second;
@ -306,7 +306,7 @@ bool bearer_cfg_handler::modify_erab(uint8_t
return true;
}
void bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id)
int bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id)
{
auto it = erabs.find(erab_id);
if (it != erabs.end()) {
@ -314,10 +314,11 @@ void bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id)
add_gtpu_bearer(erab_id, it->second.teid_out, it->second.address.to_number(), nullptr);
if (teidin.has_value()) {
it->second.teid_in = teidin.value();
return;
return SRSRAN_SUCCESS;
}
}
logger->error("Adding erab_id=%d to GTPU", erab_id);
return SRSRAN_ERROR;
}
srsran::expected<uint32_t> bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id,

@ -138,7 +138,8 @@ std::string to_string(const cells_to_add_mod_s& obj)
* @return rnti of created ue
*/
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)
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container,
asn1::s1ap::cause_c& cause)
{
// TODO: Decision Making on whether the same QoS of the source eNB can be provided by target eNB
@ -147,6 +148,7 @@ uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s&
const enb_cell_common* target_cell = cell_common_list->get_cell_id(rrc_details::eci_to_cellid(target_eci));
if (target_cell == nullptr) {
logger.error("The S1-handover target cell_id=0x%x does not exist", rrc_details::eci_to_cellid(target_eci));
cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::ho_target_not_allowed;
return SRSRAN_INVALID_RNTI;
}
@ -162,6 +164,7 @@ uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s&
uint16_t rnti = mac->reserve_new_crnti(ue_cfg);
if (rnti == SRSRAN_INVALID_RNTI) {
logger.error("Failed to allocate C-RNTI resources");
cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::radio_res_not_available;
return SRSRAN_INVALID_RNTI;
}
@ -177,7 +180,6 @@ uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s&
// rrc_ptr->logger.error("Failed to setup e-RABs for rnti=0x%x", );
// }
// TODO: KeNB derivations
if (not ue_ptr->mobility_handler->start_s1_tenb_ho(msg, container)) {
rem_user_thread(rnti);
return SRSRAN_INVALID_RNTI;
@ -672,28 +674,32 @@ void rrc::ue::rrc_mobility::s1_source_ho_st::handle_ho_cancel(const ho_cancel_ev
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;
asn1::s1ap::cause_c failure_cause;
std::vector<asn1::s1ap::erab_item_s> not_admitted_erabs;
auto& fwd_tunnels = get_state<s1_target_ho_st>()->pending_tunnels;
fwd_tunnels.clear();
/* TS 36.331 10.2.2. - Decode HandoverPreparationInformation */
asn1::cbit_ref bref{rrc_container.data(), rrc_container.size()};
asn1::rrc::ho_prep_info_s hoprep;
if (hoprep.unpack(bref) != asn1::SRSASN_SUCCESS) {
rrc_enb->logger.error("Failed to decode HandoverPreparationinformation in S1AP SourceENBToTargetENBContainer");
trigger(srsran::failure_ev{});
failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error;
trigger(ho_failure_ev{failure_cause});
return;
}
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->logger.error("Only release 8 supported");
trigger(srsran::failure_ev{});
failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::semantic_error;
trigger(ho_failure_ev{failure_cause});
return;
}
rrc_enb->log_rrc_message("HandoverPreparation", direction_t::fromS1AP, rrc_container, hoprep, "HandoverPreparation");
/* Setup UE current state in TeNB based on HandoverPreparation message */
const ho_prep_info_r8_ies_s& hoprep_r8 = hoprep.crit_exts.c1().ho_prep_info_r8();
if (not apply_ho_prep_cfg(hoprep_r8, *ho_req.ho_req_msg)) {
return;
}
apply_ho_prep_cfg(hoprep_r8, *ho_req.ho_req_msg, not_admitted_erabs);
/* Prepare Handover Request Acknowledgment - Handover Command */
dl_dcch_msg_s dl_dcch_msg;
@ -721,13 +727,16 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev&
srsran::unique_byte_buffer_t ho_cmd_pdu = srsran::make_byte_buffer();
if (ho_cmd_pdu == nullptr) {
logger.error("Couldn't allocate PDU in %s().", __FUNCTION__);
trigger(srsran::failure_ev{});
failure_cause.set_radio_network().value =
asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell;
trigger(ho_failure_ev{failure_cause});
return;
}
asn1::bit_ref bref2{ho_cmd_pdu->msg, ho_cmd_pdu->get_tailroom()};
if (dl_dcch_msg.pack(bref2) != asn1::SRSASN_SUCCESS) {
logger.error("Failed to pack HandoverCommand");
trigger(srsran::failure_ev{});
failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error;
trigger(ho_failure_ev{failure_cause});
return;
}
ho_cmd_pdu->N_bytes = bref2.distance_bytes();
@ -740,7 +749,8 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev&
bref2 = {ho_cmd_pdu->msg, ho_cmd_pdu->get_tailroom()};
if (ho_cmd.pack(bref2) != asn1::SRSASN_SUCCESS) {
logger.error("Failed to pack HandoverCommand");
trigger(srsran::failure_ev{});
failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error;
trigger(ho_failure_ev{failure_cause});
return;
}
ho_cmd_pdu->N_bytes = bref2.distance_bytes();
@ -756,8 +766,6 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev&
// Set admitted E-RABs
std::vector<asn1::s1ap::erab_admitted_item_s> admitted_erabs;
auto& fwd_tunnels = get_state<s1_target_ho_st>()->pending_tunnels;
fwd_tunnels.clear();
for (const auto& erab : rrc_ue->bearer_list.get_erabs()) {
admitted_erabs.emplace_back();
asn1::s1ap::erab_admitted_item_s& admitted_erab = admitted_erabs.back();
@ -766,8 +774,8 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev&
// 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(
const auto& lst = ho_req.transparent_container->erab_info_list;
const 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) {
@ -776,7 +784,7 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev&
if (it == lst.end()) {
continue;
}
auto& fwd_erab = it->value.erab_info_list_item();
const 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) {
@ -787,9 +795,13 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev&
srsran::expected<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);
if (not dl_teid_in.has_value()) {
logger.error("Failed to allocate GTPU TEID");
trigger(srsran::failure_ev{});
return;
logger.error("Failed to allocate GTPU TEID for E-RAB id=%d", fwd_erab.erab_id);
not_admitted_erabs.emplace_back();
not_admitted_erabs.back().erab_id = erab.second.id;
not_admitted_erabs.back().cause.set_radio_network().value =
asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell;
admitted_erabs.pop_back();
continue;
}
fwd_tunnels.push_back(dl_teid_in.value());
srsran::uint32_to_uint8(dl_teid_in.value(), admitted_erabs.back().dl_g_tp_teid.data());
@ -802,42 +814,60 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev&
rrc_ue->rnti,
rrc_ue->ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx,
std::move(ho_cmd_pdu),
admitted_erabs)) {
trigger(srsran::failure_ev{});
admitted_erabs,
not_admitted_erabs)) {
failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error;
trigger(ho_failure_ev{failure_cause});
return;
}
}
bool rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& ho_prep,
const asn1::s1ap::ho_request_s& ho_req_msg)
void rrc::ue::rrc_mobility::handle_ho_failure(s1_target_ho_st& s, const ho_failure_ev& ev)
{
// Store Handover failure cause
s.failure_cause = ev.cause;
}
void rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& ho_prep,
const asn1::s1ap::ho_request_s& ho_req_msg,
std::vector<asn1::s1ap::erab_item_s>& erabs_failed_to_setup)
{
const ue_cell_ded* target_cell = rrc_ue->ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX);
const cell_cfg_t& target_cell_cfg = target_cell->cell_common->cell_cfg;
// Establish ERABs/DRBs
for (const auto& erab_item : ho_req_msg.protocol_ies.erab_to_be_setup_list_ho_req.value) {
auto& erab = erab_item.value.erab_to_be_setup_item_ho_req();
const auto& erab = erab_item.value.erab_to_be_setup_item_ho_req();
if (erab.ext) {
get_logger().warning("Not handling E-RABToBeSetupList extensions");
}
if (erab.transport_layer_address.length() > 32) {
get_logger().error("IPv6 addresses not currently supported");
trigger(srsran::failure_ev{});
return false;
}
if (not erab.ie_exts_present or not erab.ie_exts.data_forwarding_not_possible_present or
erab.ie_exts.data_forwarding_not_possible.ext.value !=
asn1::s1ap::data_forwarding_not_possible_opts::data_forwarding_not_possible) {
get_logger().warning("Data Forwarding of E-RABs not supported");
erabs_failed_to_setup.emplace_back();
erabs_failed_to_setup.back().erab_id = erab.erab_id;
erabs_failed_to_setup.back().cause.set_transport().value = asn1::s1ap::cause_transport_opts::unspecified;
continue;
}
// Create E-RAB and associated main GTPU tunnel
uint32_t teid_out;
uint32_t teid_out = 0;
srsran::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(erab.erab_id);
if (rrc_ue->bearer_list.add_erab(
erab.erab_id, erab.erab_level_qos_params, erab.transport_layer_address, teid_out, nullptr) !=
SRSRAN_SUCCESS) {
erabs_failed_to_setup.emplace_back();
erabs_failed_to_setup.back().erab_id = erab.erab_id;
erabs_failed_to_setup.back().cause.set_radio_network().value =
asn1::s1ap::cause_radio_network_opts::invalid_qos_combination;
continue;
}
if (rrc_ue->bearer_list.add_gtpu_bearer(erab.erab_id) != SRSRAN_SUCCESS) {
erabs_failed_to_setup.emplace_back();
erabs_failed_to_setup.back().erab_id = erab.erab_id;
erabs_failed_to_setup.back().cause.set_radio_network().value =
asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell;
continue;
}
}
// Regenerate AS Keys
@ -876,8 +906,6 @@ bool rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& ho
// Save source UE MAC configuration as a base
rrc_ue->mac_ctrl.handle_ho_prep(ho_prep);
return true;
}
void rrc::ue::rrc_mobility::handle_recfg_complete(wait_recfg_comp& s, const recfg_complete_ev& ev)

@ -979,13 +979,9 @@ bool s1ap::handle_handover_command(const asn1::s1ap::ho_cmd_s& msg)
bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg)
{
uint16_t rnti = SRSRAN_INVALID_RNTI;
auto on_scope_exit = srsran::make_scope_exit([this, &rnti, msg]() {
// If rnti is not allocated successfully, remove from s1ap and send handover failure
if (rnti == SRSRAN_INVALID_RNTI) {
send_ho_failure(msg.protocol_ies.mme_ue_s1ap_id.value.value);
}
});
uint32_t mme_ue_s1ap_id = msg.protocol_ies.mme_ue_s1ap_id.value.value;
asn1::s1ap::cause_c cause;
cause.set_misc().value = cause_misc_opts::unspecified;
if (msg.ext or msg.protocol_ies.ho_restrict_list_present) {
logger.warning("Not handling S1AP Handover Request extensions or Handover Restriction List");
@ -993,6 +989,8 @@ bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg)
if (msg.protocol_ies.handov_type.value.value != handov_type_opts::intralte) {
logger.error("Not handling S1AP non-intra LTE handovers");
cause.set_radio_network().value = cause_radio_network_opts::interrat_redirection;
send_ho_failure(mme_ue_s1ap_id, cause);
return false;
}
@ -1000,31 +998,37 @@ bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg)
if (users.find_ue_mmeid(msg.protocol_ies.mme_ue_s1ap_id.value.value) != nullptr) {
logger.error("The provided MME_UE_S1AP_ID=%" PRIu64 " is already connected to the cell",
msg.protocol_ies.mme_ue_s1ap_id.value.value);
cause.set_radio_network().value = cause_radio_network_opts::unknown_mme_ue_s1ap_id;
send_ho_failure(mme_ue_s1ap_id, cause);
return false;
}
// Create user ctxt object and associated MME context
std::unique_ptr<ue> ue_ptr{new ue{this}};
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;
}
srsran_assert(users.add_user(std::move(ue_ptr)) != nullptr, "Unexpected failure to create S1AP UE");
// 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) {
logger.error("Failed to unpack SourceToTargetTransparentContainer");
logger.warning("Failed to unpack SourceToTargetTransparentContainer");
cause.set_protocol().value = cause_protocol_opts::transfer_syntax_error;
send_ho_failure(mme_ue_s1ap_id, cause);
return false;
}
// Handle Handover Resource Allocation
rnti = rrc->start_ho_ue_resource_alloc(msg, container);
return rnti != SRSRAN_INVALID_RNTI;
rnti = rrc->start_ho_ue_resource_alloc(msg, container, cause);
if (rnti == SRSRAN_INVALID_RNTI) {
send_ho_failure(mme_ue_s1ap_id, cause);
return false;
}
return true;
}
bool s1ap::send_ho_failure(uint32_t mme_ue_s1ap_id)
void s1ap::send_ho_failure(uint32_t mme_ue_s1ap_id, const asn1::s1ap::cause_c& cause)
{
// Remove created s1ap user
ue* u = users.find_ue_mmeid(mme_ue_s1ap_id);
@ -1037,17 +1041,17 @@ bool s1ap::send_ho_failure(uint32_t mme_ue_s1ap_id)
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;
container.cause.value = cause;
return sctp_send_s1ap_pdu(tx_pdu, SRSRAN_INVALID_RNTI, "HandoverFailure");
sctp_send_s1ap_pdu(tx_pdu, SRSRAN_INVALID_RNTI, "HandoverFailure");
}
bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
uint16_t rnti,
uint32_t enb_cc_idx,
srsran::unique_byte_buffer_t ho_cmd,
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers)
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers,
srsran::const_span<asn1::s1ap::erab_item_s> not_admitted_bearers)
{
s1ap_pdu_c tx_pdu;
tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_HO_RES_ALLOC);
@ -1079,6 +1083,19 @@ bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg,
}
}
// Add failed to Setup E-RABs
if (not not_admitted_bearers.empty()) {
container.erab_failed_to_setup_list_ho_req_ack_present = true;
container.erab_failed_to_setup_list_ho_req_ack.value.resize(not_admitted_bearers.size());
for (size_t i = 0; i < not_admitted_bearers.size(); ++i) {
container.erab_failed_to_setup_list_ho_req_ack.value[i].load_info_obj(
ASN1_S1AP_ID_ERAB_FAILEDTO_SETUP_ITEM_HO_REQ_ACK);
auto& erab = container.erab_failed_to_setup_list_ho_req_ack.value[i].value.erab_failedto_setup_item_ho_req_ack();
erab.erab_id = not_admitted_bearers[i].erab_id;
erab.cause = not_admitted_bearers[i].cause;
}
}
// Pack transparent container
asn1::s1ap::targetenb_to_sourceenb_transparent_container_s transparent_container;
transparent_container.rrc_container.resize(ho_cmd->N_bytes);

@ -123,7 +123,8 @@ public:
uint16_t rnti,
uint32_t enb_cc_idx,
srsran::unique_byte_buffer_t ho_cmd,
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers) override
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers,
srsran::const_span<asn1::s1ap::erab_item_s> not_admitted_bearers) override
{
return true;
}
@ -184,9 +185,9 @@ public:
const asn1::s1ap::ho_cmd_s& msg,
srsran::unique_byte_buffer_t 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
uint16_t start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg,
const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container,
asn1::s1ap::cause_c& failure_cause) override
{
return SRSRAN_INVALID_RNTI;
}

@ -27,7 +27,8 @@ struct mobility_test_params {
concurrent_ho,
ho_prep_failure,
duplicate_crnti_ce,
recover
recover,
wrong_target_cell,
} fail_at;
const char* to_string()
{
@ -44,6 +45,8 @@ struct mobility_test_params {
return "fail and success";
case test_event::duplicate_crnti_ce:
return "duplicate CRNTI CE";
case test_event::wrong_target_cell:
return "wrong target cell";
default:
return "none";
}
@ -271,7 +274,11 @@ int test_s1ap_tenb_mobility(mobility_test_params test_params)
erab.erab_id = 5;
erab.erab_level_qos_params.qci = 9;
asn1::s1ap::sourceenb_to_targetenb_transparent_container_s container;
if (test_params.fail_at == mobility_test_params::test_event::wrong_target_cell) {
container.target_cell_id.cell_id.from_number(0x19C03);
} else {
container.target_cell_id.cell_id.from_number(0x19C02);
}
uint8_t ho_prep_container[] = {
0x0a, 0x10, 0x0b, 0x81, 0x80, 0x00, 0x01, 0x80, 0x00, 0xf3, 0x02, 0x08, 0x00, 0x00, 0x15, 0x80, 0x00, 0x14,
0x06, 0xa4, 0x02, 0xf0, 0x04, 0x04, 0xf0, 0x00, 0x14, 0x80, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x12, 0x31, 0xb6,
@ -289,7 +296,13 @@ int test_s1ap_tenb_mobility(mobility_test_params test_params)
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);
asn1::s1ap::cause_c cause;
int rnti = tester.rrc.start_ho_ue_resource_alloc(ho_req, container, cause);
if (test_params.fail_at == mobility_test_params::test_event::wrong_target_cell) {
TESTASSERT(rnti == SRSRAN_INVALID_RNTI);
TESTASSERT(tester.rrc.get_nof_users() == 0);
return SRSRAN_SUCCESS;
}
tester.tic();
TESTASSERT(tester.rrc.get_nof_users() == 1);
TESTASSERT(tester.mac.ue_db.count(0x46));
@ -525,12 +538,13 @@ int main(int argc, char** argv)
}
argparse::parse_args(argc, argv);
// S1AP Handover
// Source ENB - S1 Handover
TESTASSERT(test_s1ap_mobility(*spy, mobility_test_params{event::wrong_measreport}) == 0);
TESTASSERT(test_s1ap_mobility(*spy, mobility_test_params{event::concurrent_ho}) == 0);
TESTASSERT(test_s1ap_mobility(*spy, mobility_test_params{event::ho_prep_failure}) == 0);
TESTASSERT(test_s1ap_mobility(*spy, mobility_test_params{event::success}) == 0);
TESTASSERT(test_s1ap_tenb_mobility(mobility_test_params{event::wrong_target_cell}) == 0);
TESTASSERT(test_s1ap_tenb_mobility(mobility_test_params{event::success}) == 0);
// intraeNB Handover

@ -97,7 +97,8 @@ public:
uint16_t rnti,
uint32_t enb_cc_idx,
srsran::unique_byte_buffer_t ho_cmd,
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers) override
srsran::span<asn1::s1ap::erab_admitted_item_s> admitted_bearers,
srsran::const_span<asn1::s1ap::erab_item_s> not_admitted_bearers) override
{
last_ho_req_ack.rnti = rnti;
last_ho_req_ack.ho_cmd_pdu = std::move(ho_cmd);

Loading…
Cancel
Save