diff --git a/lib/include/srslte/interfaces/enb_interfaces.h b/lib/include/srslte/interfaces/enb_interfaces.h index a07e84ac2..749e626f1 100644 --- a/lib/include/srslte/interfaces/enb_interfaces.h +++ b/lib/include/srslte/interfaces/enb_interfaces.h @@ -267,6 +267,7 @@ public: virtual void write_dl_info(uint16_t rnti, srslte::unique_byte_buffer_t sdu) = 0; virtual void release_complete(uint16_t rnti) = 0; virtual bool setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg) = 0; + virtual bool modify_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT *msg) = 0; virtual bool setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg) = 0; virtual bool release_erabs(uint32_t rnti) = 0; virtual void add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID) = 0; diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index 0dd4da54f..60649e500 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -156,6 +156,7 @@ public: void write_dl_info(uint16_t rnti, srslte::unique_byte_buffer_t sdu); void release_complete(uint16_t rnti); bool setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg); + bool modify_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT *msg); bool setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg); bool release_erabs(uint32_t rnti); void add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID); @@ -254,6 +255,8 @@ public: bool connect_notified; + bool is_csfb; + private: srslte::byte_buffer_pool *pool; @@ -378,6 +381,7 @@ private: rrc_cfg_t cfg; uint32_t nof_si_messages; asn1::rrc::sib_type2_s sib2; + asn1::rrc::sib_type7_s sib7; void run_thread(); void rem_user_thread(uint16_t rnti); diff --git a/srsenb/hdr/stack/upper/s1ap.h b/srsenb/hdr/stack/upper/s1ap.h index bdcfb016a..5ad28980e 100644 --- a/srsenb/hdr/stack/upper/s1ap.h +++ b/srsenb/hdr/stack/upper/s1ap.h @@ -118,6 +118,7 @@ private: bool handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *msg); bool handle_s1setupfailure(LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *msg); bool handle_erabsetuprequest(LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg); + bool handle_uecontextmodifyrequest(LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT *msg); bool send_initialuemessage(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, @@ -132,6 +133,8 @@ private: 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_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); diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index a399e45b6..c98a5ea08 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -295,9 +295,6 @@ bool rrc::setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRE return false; } - if(msg->CSFallbackIndicator_present) { - rrc_log->warning("Not handling CSFallbackIndicator\n"); - } if(msg->AdditionalCSFallbackIndicator_present) { rrc_log->warning("Not handling AdditionalCSFallbackIndicator\n"); } @@ -346,6 +343,14 @@ bool rrc::setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRE liblte_pack(msg->SecurityKey.buffer, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN, key); users[rnti].set_security_key(key, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN/8); + // CSFB + if (msg->CSFallbackIndicator_present) { + if (msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_REQUIRED || + msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_HIGH_PRIORITY) { + users[rnti].is_csfb = true; + } + } + // Send RRC security mode command users[rnti].send_security_mode_command(); @@ -357,6 +362,75 @@ bool rrc::setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRE return true; } +bool rrc::modify_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT *msg) +{ + bool err = false; + pthread_mutex_lock(&user_mutex); + + rrc_log->info("Modifying context for 0x%x\n", rnti); + + if (users.count(rnti) == 0) { + rrc_log->warning("Unrecognised rnti: 0x%x\n", rnti); + pthread_mutex_unlock(&user_mutex); + return false; + } + + if (msg->CSFallbackIndicator_present) { + if (msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_REQUIRED || + msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_HIGH_PRIORITY) { + /* Remember that we are in a CSFB right now */ + users[rnti].is_csfb = true; + } + } + + if (msg->AdditionalCSFallbackIndicator_present) { + rrc_log->warning("Not handling AdditionalCSFallbackIndicator\n"); + err = true; + } + if (msg->CSGMembershipStatus_present) { + rrc_log->warning("Not handling CSGMembershipStatus\n"); + err = true; + } + if (msg->RegisteredLAI_present) { + rrc_log->warning("Not handling RegisteredLAI\n"); + err = true; + } + if (msg->SubscriberProfileIDforRFP_present) { + rrc_log->warning("Not handling SubscriberProfileIDforRFP\n"); + err = true; + } + + if (err) { + // maybe pass a cause value? + return false; + } + + // UEAggregateMaximumBitrate + if (msg->uEaggregateMaximumBitrate_present) { + users[rnti].set_bitrates(&msg->uEaggregateMaximumBitrate); + } + + // UESecurityCapabilities + if (msg->UESecurityCapabilities_present) { + users[rnti].set_security_capabilities(&msg->UESecurityCapabilities); + } + + // SecurityKey + if (msg->SecurityKey_present) { + uint8_t key[32]; + liblte_pack(msg->SecurityKey.buffer, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN, key); + users[rnti].set_security_key(key, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN / 8); + + // Send RRC security mode command ?? + users[rnti].send_security_mode_command(); + } + + pthread_mutex_unlock(&user_mutex); + + return true; +} + + bool rrc::setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg) { pthread_mutex_lock(&user_mutex); @@ -742,6 +816,10 @@ uint32_t rrc::generate_sibs() log_rrc_message("SIB payload", Tx, sib_buffer[msg_index].get(), msg[msg_index]); } + if (cfg.sibs[6].type() == asn1::rrc::sys_info_r8_ies_s::sib_type_and_info_item_c_::types::sib7) { + sib7 = cfg.sibs[6].sib7(); + } + return nof_messages; } @@ -952,6 +1030,7 @@ rrc::ue::ue() integ_algo = srslte::INTEGRITY_ALGORITHM_ID_EIA0; cipher_algo = srslte::CIPHERING_ALGORITHM_ID_EEA0; nas_pending = false; + is_csfb = false; state = RRC_STATE_IDLE; pool = srslte::byte_buffer_pool::get_instance(); } @@ -1565,6 +1644,12 @@ void rrc::ue::send_connection_release() dl_dcch_msg.msg.c1().rrc_conn_release().rrc_transaction_id = (uint8_t)((transaction_id++) % 4); dl_dcch_msg.msg.c1().rrc_conn_release().crit_exts.set_c1().set_rrc_conn_release_r8(); dl_dcch_msg.msg.c1().rrc_conn_release().crit_exts.c1().rrc_conn_release_r8().release_cause = release_cause_e::other; + if (is_csfb) { + rrc_conn_release_r8_ies_s& rel_ies = dl_dcch_msg.msg.c1().rrc_conn_release().crit_exts.c1().rrc_conn_release_r8(); + rel_ies.redirected_carrier_info_present = true; + rel_ies.redirected_carrier_info.set_geran(); + rel_ies.redirected_carrier_info.geran() = parent->sib7.carrier_freqs_info_list[0].carrier_freqs; + } send_dl_dcch(&dl_dcch_msg); } diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index 6bfdae822..c94c3b059 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -435,6 +435,8 @@ bool s1ap::handle_initiatingmessage(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg) return handle_paging(&msg->choice.Paging); case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABSETUPREQUEST: return handle_erabsetuprequest(&msg->choice.E_RABSetupRequest); + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTMODIFICATIONREQUEST: + return handle_uecontextmodifyrequest(&msg->choice.UEContextModificationRequest); default: s1ap_log->error("Unhandled intiating message: %s\n", liblte_s1ap_initiatingmessage_choice_text[msg->choice_type]); } @@ -526,6 +528,23 @@ bool s1ap::handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETU return false; } + /* Ideally the check below would be "if (users[rnti].is_csfb)" */ + if (msg->CSFallbackIndicator_present) { + if (msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_REQUIRED || + msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_HIGH_PRIORITY) { + // Send RRC Release (cs-fallback-triggered) to MME + LIBLTE_S1AP_CAUSE_STRUCT cause; + cause.ext = false; + cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK; + cause.choice.radioNetwork.ext = false; + cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_CS_FALLBACK_TRIGGERED; + + /* FIXME: This should normally probably only be sent after the SecurityMode procedure has completed! */ + ue_ctxt_map[rnti].release_requested = true; + send_uectxtreleaserequest(rnti, &cause); + } + } + return true; } @@ -568,6 +587,53 @@ bool s1ap::handle_erabsetuprequest(LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT return true; } +bool s1ap::handle_uecontextmodifyrequest(LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT* msg) +{ + s1ap_log->info("Received UeContextModificationRequest\n"); + if (enbid_to_rnti_map.end() == enbid_to_rnti_map.find(msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID)) { + s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); + return false; + } + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID]; + if (msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ue_ctxt_map[rnti].MME_UE_S1AP_ID) { + s1ap_log->warning("MME_UE_S1AP_ID has changed - old:%d, new:%d\n", + ue_ctxt_map[rnti].MME_UE_S1AP_ID, + msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID); + ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + } + + if (!rrc->modify_ue_ctxt(rnti, msg)) { + LIBLTE_S1AP_CAUSE_STRUCT cause; + cause.ext = false; + cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_MISC; + cause.choice.misc.ext = false; + cause.choice.misc.e = LIBLTE_S1AP_CAUSEMISC_UNSPECIFIED; + send_uectxmodifyfailure(rnti, &cause); + return true; + } + + // Send UEContextModificationResponse + send_uectxmodifyresp(rnti); + + /* Ideally the check below would be "if (users[rnti].is_csfb)" */ + if (msg->CSFallbackIndicator_present) { + if (msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_REQUIRED || + msg->CSFallbackIndicator.e == LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_HIGH_PRIORITY) { + // Send RRC Release (cs-fallback-triggered) to MME + LIBLTE_S1AP_CAUSE_STRUCT cause; + cause.ext = false; + cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK; + cause.choice.radioNetwork.ext = false; + cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_CS_FALLBACK_TRIGGERED; + + ue_ctxt_map[rnti].release_requested = true; + send_uectxtreleaserequest(rnti, &cause); + } + } + + return true; +} + bool s1ap::handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *msg) { s1ap_log->info("Received UEContextReleaseCommand\n"); @@ -982,6 +1048,103 @@ bool s1ap::send_initial_ctxt_setup_failure(uint16_t rnti) return true; } +bool s1ap::send_uectxmodifyresp(uint16_t rnti) +{ + if (!mme_connected) { + return false; + } + srslte::unique_byte_buffer_t buf = srslte::allocate_unique_buffer(*pool); + if (!buf) { + s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::send_uectxmodifyresp().\n"); + return false; + } + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME; + + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT* succ = &tx_pdu.choice.successfulOutcome; + succ->procedureCode = LIBLTE_S1AP_PROC_ID_UECONTEXTMODIFICATION; + succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTMODIFICATIONRESPONSE; + + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONRESPONSE_STRUCT* resp = &succ->choice.UEContextModificationResponse; + resp->ext = false; + resp->CriticalityDiagnostics_present = false; + + resp->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + resp->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get()); + s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending ContextModificationFailure for RNTI:0x%x", rnti); + + 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 ContextModificationFailure for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_uectxmodifyfailure(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT* cause) +{ + if (!mme_connected) { + return false; + } + srslte::unique_byte_buffer_t buf = srslte::allocate_unique_buffer(*pool); + if (!buf) { + s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::send_initial_ctxt_setup_failure().\n"); + return false; + } + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME; + + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT* unsucc = &tx_pdu.choice.unsuccessfulOutcome; + unsucc->procedureCode = LIBLTE_S1AP_PROC_ID_UECONTEXTMODIFICATION; + unsucc->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_UECONTEXTMODIFICATIONFAILURE; + + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONFAILURE_STRUCT* fail = &unsucc->choice.UEContextModificationFailure; + fail->ext = false; + fail->CriticalityDiagnostics_present = false; + + fail->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + fail->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + memcpy(&fail->Cause, cause, sizeof(LIBLTE_S1AP_CAUSE_STRUCT)); + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get()); + s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending UEContextModificationFailure for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, + buf->msg, + buf->N_bytes, + (struct sockaddr*)&mme_addr, + sizeof(struct sockaddr_in), + htonl(PPID), + 0, + ue_ctxt_map[rnti].stream_id, + 0, + 0); + + if (n_sent == -1) { + s1ap_log->error("Failed to send UEContextModificationFailure for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} //bool s1ap::send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) //{