Created new class to hold all NAS states for the NAS state machine. Added EMM-DEREGISTERED substates and EMM-REGISTERED substates.

Decoupled PLMN selection, attach request and service request. Removed RRC connect procedure from NAS.
master
Pedro Alvarez 5 years ago committed by Andre Puschmann
parent ff7811c822
commit 14844a168a

@ -236,12 +236,14 @@ public:
virtual bool connection_request_completed(bool outcome) = 0;
};
// NAS interface for UE
class nas_interface_ue
// NAS interface for the Stack
class nas_interface_stack
{
public:
virtual void start_attach_proc(srslte::proc_state_t* proc_result, srslte::establishment_cause_t cause_) = 0;
virtual bool detach_request(const bool switch_off) = 0;
virtual bool switch_on() = 0;
virtual bool switch_off() = 0;
virtual bool enable_data() = 0;
virtual bool disable_data() = 0;
};
// PDCP interface for RRC
@ -621,7 +623,8 @@ public:
class stack_interface_gw : public pdcp_interface_gw
{
public:
virtual bool switch_on() = 0;
virtual bool is_attached() = 0;
virtual bool start_service_request() = 0;
};
class gw_interface_stack : public gw_interface_nas, public gw_interface_rrc, public gw_interface_pdcp

@ -65,6 +65,10 @@ public:
std::string get_type() final;
bool get_metrics(srsenb::stack_metrics_t* metrics) final;
// GW srsue stack_interface_gw dummy interface
bool is_attached(){return true;};
bool start_service_request(){return true;};
// PHY->MAC interface
int sf_indication(const uint32_t tti);
int rx_data_indication(rx_data_ind_t& grant);

@ -68,6 +68,8 @@ public:
int init(const stack_args_t& args_, srslte::logger* logger_, phy_interface_stack_lte* phy_, gw_interface_stack* gw_);
bool switch_on() final;
bool switch_off() final;
bool is_attached() final;
bool start_service_request() final;
bool enable_data();
bool disable_data();
void stop() final;

@ -70,6 +70,10 @@ public:
bool switch_off() final;
void stop();
// GW srsue stack_interface_gw dummy interface
bool is_attached(){return true;};
bool start_service_request(){return true;};
bool get_metrics(stack_metrics_t* metrics);
bool is_rrc_connected();

@ -38,42 +38,46 @@ using srslte::byte_buffer_t;
namespace srsue {
class nas : public nas_interface_rrc, public nas_interface_ue, public srslte::timer_callback
class nas : public nas_interface_rrc, public nas_interface_stack, public srslte::timer_callback
{
public:
explicit nas(srslte::task_sched_handle task_sched_);
virtual ~nas() = default;
virtual ~nas();
void init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& args_);
void stop();
void run_tti();
void get_metrics(nas_metrics_t* m);
emm_state_t get_state();
// Metrics getter
void get_metrics(nas_metrics_t* m);
// RRC interface
void left_rrc_connected() override;
bool connection_request_completed(bool outcome) override;
bool paging(srslte::s_tmsi_t* ue_identity) override;
void set_barring(srslte::barring_t barring) override;
void write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) override;
uint32_t get_k_enb_count() override;
bool is_attached() override;
bool get_k_asme(uint8_t* k_asme_, uint32_t n) override;
uint32_t get_ipv4_addr() override;
bool get_ipv6_addr(uint8_t* ipv6_addr) override;
void plmn_search_completed(const rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS],
int nof_plmns) final;
// UE interface
void start_attach_proc(srslte::proc_state_t* result, srslte::establishment_cause_t cause_) final;
bool detach_request(const bool switch_off) final;
// Stack interface
bool switch_on();
bool switch_off();
bool enable_data();
bool disable_data();
void start_service_request(srslte::establishment_cause_t cause_);
void plmn_search_completed(const rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS],
int nof_plmns) final;
bool connection_request_completed(bool outcome) final;
// Stack+RRC interface
bool is_attached() override;
// timer callback
void timer_expired(uint32_t timeout_id) override;
// PCAP
void start_pcap(srslte::nas_pcap* pcap_);
void start_pcap(srslte::nas_pcap* pcap_) { pcap = pcap_; }
private:
srslte::byte_buffer_pool* pool = nullptr;
@ -82,13 +86,13 @@ private:
usim_interface_nas* usim = nullptr;
gw_interface_nas* gw = nullptr;
nas_args_t cfg = {};
bool running = false;
emm_state_t state = EMM_STATE_DEREGISTERED;
nas_args_t cfg = {};
emm_state_t state = {};
srslte::barring_t current_barring = srslte::barring_t::none;
bool plmn_is_selected = false;
srslte::plmn_id_t current_plmn;
srslte::plmn_id_t home_plmn;
@ -168,8 +172,7 @@ private:
// PCAP
srslte::nas_pcap* pcap = nullptr;
bool running = false;
// Security
void
integrity_generate(uint8_t* key_128, uint32_t count, uint8_t direction, uint8_t* msg, uint32_t msg_len, uint8_t* mac);
bool integrity_check(srslte::byte_buffer_t* pdu);
@ -177,11 +180,15 @@ private:
void cipher_decrypt(srslte::byte_buffer_t* pdu);
int apply_security_config(srslte::unique_byte_buffer_t& pdu, uint8_t sec_hdr_type);
void reset_security_context();
void set_k_enb_count(uint32_t count);
bool check_cap_replay(LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT* caps);
// NAS Connection Initiation/Termination
void start_attach_request(srslte::establishment_cause_t cause_);
bool detach_request(const bool switch_off);
// PLMN Selection Helpers
void start_plmn_selection_proc();
void select_plmn();
// Parsers
@ -227,7 +234,9 @@ private:
// Other internal helpers
void enter_state(emm_state_t state_);
void enter_emm_deregistered();
void enter_emm_null();
void enter_emm_deregistered(emm_state_t::deregistered_substate_t substate);
void enter_emm_deregistered_initiated();
// security context persistence file
bool read_ctxt_file(nas_sec_ctxt* ctxt);
@ -266,60 +275,25 @@ private:
return true;
}
class rrc_connect_proc
std::vector<uint8_t> split_string(const std::string input)
{
public:
using rrc_connect_complete_ev = srslte::proc_state_t;
struct connection_request_completed_t {
bool outcome;
};
struct attach_timeout {};
explicit rrc_connect_proc(nas* nas_ptr_);
srslte::proc_outcome_t init(srslte::establishment_cause_t cause_, srslte::unique_byte_buffer_t pdu);
srslte::proc_outcome_t step();
void then(const srslte::proc_state_t& result);
srslte::proc_outcome_t react(connection_request_completed_t event);
srslte::proc_outcome_t react(attach_timeout event);
static const char* name() { return "RRC Connect"; }
private:
static const uint32_t attach_timeout_ms = 5000;
nas* nas_ptr;
srslte::timer_handler::unique_timer timeout_timer;
enum class state_t { conn_req, wait_attach } state;
};
class plmn_search_proc
{
public:
struct plmn_search_complete_t {
rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS];
int nof_plmns;
plmn_search_complete_t(const rrc_interface_nas::found_plmn_t* plmns_, int nof_plmns_) : nof_plmns(nof_plmns_)
{
if (nof_plmns > 0) {
std::copy(&plmns_[0], &plmns_[nof_plmns], found_plmns);
}
std::vector<uint8_t> list;
std::stringstream ss(input);
while (ss.good()) {
std::string substr;
getline(ss, substr, ',');
if (not substr.empty()) {
list.push_back(strtol(substr.c_str(), nullptr, 10));
}
};
explicit plmn_search_proc(nas* nas_ptr_) : nas_ptr(nas_ptr_) {}
srslte::proc_outcome_t init();
srslte::proc_outcome_t step();
void then(const srslte::proc_state_t& result);
srslte::proc_outcome_t react(const plmn_search_complete_t& t);
srslte::proc_outcome_t react(const rrc_connect_proc::rrc_connect_complete_ev& t);
static const char* name() { return "PLMN Search"; }
private:
nas* nas_ptr;
enum class state_t { plmn_search, rrc_connect } state = state_t::plmn_search;
};
}
return list;
}
// NAS Idle procedures
class plmn_search_proc; // PLMN selection proc (fwd declared)
srslte::proc_manager_list_t callbacks;
srslte::proc_t<plmn_search_proc> plmn_searcher;
srslte::proc_t<rrc_connect_proc> rrc_connector;
const std::string gw_setup_failure_str = "Failed to setup/configure GW interface";
};

@ -22,6 +22,10 @@
#ifndef SRSUE_NAS_COMMON_H
#define SRSUE_NAS_COMMON_H
#include <string>
#include <atomic>
#include "srslte/common/logmap.h"
namespace srsue {
typedef struct {
@ -44,21 +48,197 @@ public:
nas_sim_args_t sim;
};
// EMM states (3GPP 24.302 v10.0.0)
typedef enum {
EMM_STATE_NULL = 0,
EMM_STATE_DEREGISTERED,
EMM_STATE_REGISTERED,
EMM_STATE_DEREGISTERED_INITIATED,
EMM_STATE_TAU_INITIATED,
EMM_STATE_N_ITEMS,
} emm_state_t;
static const char emm_state_text[EMM_STATE_N_ITEMS][100] = {"NULL",
"DEREGISTERED",
"REGISTERED",
"DEREGISTERED INITIATED",
"TRACKING AREA UPDATE INITIATED"};
// EMM states (3GPP 24.302 v14.0.0)
class emm_state_t
{
public:
enum class state_t {
null = 0,
deregistered,
deregistered_initiated,
registered,
registered_initiated,
tau_initiated,
service_request_initiated,
};
// EMM-DEREGISTERED sub-states (3GPP 24.302 v14.0.0)
enum class deregistered_substate_t {
null = 0, // This should be used when not in EMM-DEREGISTERED
normal_service,
limited_service,
attempting_to_attach,
plmn_search,
no_imsi,
attach_needed,
no_cell_available,
e_call_inactive,
};
} // namespace srsue
// EMM-REGISTERED sub-states (3GPP 24.302 v14.0.0)
enum class registered_substate_t {
null = 0, // This should be used when not in EMM-REGISTERED
normal_service,
attempting_to_update,
limited_service,
plmn_search,
update_needed,
no_cell_available,
attempting_to_update_mm,
imsi_dettach_initiated,
};
// FSM setters
void set_null()
{
state = state_t::null;
deregistered_substate = deregistered_substate_t::null;
registered_substate = registered_substate_t::null;
nas_log->debug("Changed to EMM state: %s\n", get_full_state_text().c_str());
}
void set_deregistered(deregistered_substate_t substate)
{
state = state_t::deregistered;
deregistered_substate = substate;
registered_substate = registered_substate_t::null;
nas_log->debug("Changed to EMM state: %s\n", get_full_state_text().c_str());
}
void set_deregistered_initiated()
{
state = state_t::deregistered_initiated;
deregistered_substate = deregistered_substate_t::null;
registered_substate = registered_substate_t::null;
nas_log->debug("Changed to EMM state: %s\n", get_full_state_text().c_str());
}
void set_registered(registered_substate_t substate)
{
state = state_t::registered;
deregistered_substate = deregistered_substate_t::null;
registered_substate = substate;
nas_log->debug("Changed to EMM state: %s\n", get_full_state_text().c_str());
}
void set_registered_initiated()
{
state = state_t::registered_initiated;
deregistered_substate = deregistered_substate_t::null;
registered_substate = registered_substate_t::null;
nas_log->debug("Changed to EMM state: %s\n", get_full_state_text().c_str());
}
void set_tau_initiated()
{
state = state_t::tau_initiated;
deregistered_substate = deregistered_substate_t::null;
registered_substate = registered_substate_t::null;
nas_log->debug("Changed to EMM state: %s\n", get_full_state_text().c_str());
}
void set_service_request_initiated()
{
state = state_t::service_request_initiated;
deregistered_substate = deregistered_substate_t::null;
registered_substate = registered_substate_t::null;
nas_log->debug("Changed to EMM state: %s\n", get_full_state_text().c_str());
}
// FSM getters
state_t get_state() { return state; }
deregistered_substate_t get_deregistered_substate() { return deregistered_substate; }
registered_substate_t get_registered_substate() { return registered_substate; }
// Text Helpers
const std::string get_full_state_text();
private:
std::atomic<state_t> state{state_t::null}; // The GW might require to know the NAS state from another thread
deregistered_substate_t deregistered_substate = deregistered_substate_t::null;
registered_substate_t registered_substate = registered_substate_t::null;
srslte::log_ref nas_log{"NAS"};
};
inline const char* emm_state_text(emm_state_t::state_t type)
{
switch (type) {
case emm_state_t::state_t::null:
return "NULL";
case emm_state_t::state_t::deregistered:
return "DEREGISTERED";
case emm_state_t::state_t::deregistered_initiated:
return "DEREGISTERED-INITIATED";
case emm_state_t::state_t::registered:
return "REGISTERED";
case emm_state_t::state_t::registered_initiated:
return "REGISTERED-INITIATED";
case emm_state_t::state_t::tau_initiated:
return "TAU-INITIATED";
case emm_state_t::state_t::service_request_initiated:
return "SERVICE-REQUEST-INITIATED";
}
return "INVALID";
}
inline const char* emm_deregistered_substate_text(emm_state_t::deregistered_substate_t type)
{
switch (type) {
case emm_state_t::deregistered_substate_t::null:
return "NULL";
case emm_state_t::deregistered_substate_t::normal_service:
return "NORMAL-SERVICE";
case emm_state_t::deregistered_substate_t::limited_service:
return "LIMITED-SERVICE";
case emm_state_t::deregistered_substate_t::attempting_to_attach:
return "ATTEMPTING-TO-ATTACH";
case emm_state_t::deregistered_substate_t::plmn_search:
return "PLMN-SEARCH";
case emm_state_t::deregistered_substate_t::no_imsi:
return "NO-IMSI";
case emm_state_t::deregistered_substate_t::attach_needed:
return "ATTACH-NEEDED";
case emm_state_t::deregistered_substate_t::no_cell_available:
return "NO-CELL-AVAILABLE";
case emm_state_t::deregistered_substate_t::e_call_inactive:
return "eCALL-INACTIVE";
}
return "INVALID";
}
inline const char* emm_registered_substate_text(emm_state_t::registered_substate_t type)
{
switch (type) {
case emm_state_t::registered_substate_t::null:
return "NULL";
case emm_state_t::registered_substate_t::normal_service:
return "NORMAL-SERVICE";
case emm_state_t::registered_substate_t::attempting_to_update:
return "ATTEMPTING-TO-UPDATE";
case emm_state_t::registered_substate_t::limited_service:
return "LIMITED-SERVICE";
case emm_state_t::registered_substate_t::plmn_search:
return "PLMN-SEARCH";
case emm_state_t::registered_substate_t::update_needed:
return "UPDATE-NEEDED";
case emm_state_t::registered_substate_t::no_cell_available:
return "NO-CELL-AVAILABLE";
case emm_state_t::registered_substate_t::attempting_to_update_mm:
return "ATTEMPTING-TO-UPDATE-MM";
case emm_state_t::registered_substate_t::imsi_dettach_initiated:
return "IMSI-DETTACHED-INITIATED";
}
return "INVALID";
}
inline const std::string emm_state_t::get_full_state_text()
{
if (state == state_t::deregistered) {
return emm_state_text(state) + std::string(", with substate ") +
emm_deregistered_substate_text(deregistered_substate);
} else if (state == state_t::registered) {
return emm_state_text(state) + std::string(", with substate ") +
emm_registered_substate_text(registered_substate);
} else {
return emm_state_text(state);
}
return std::string("Invalid State");
};
} // namespace srsue
#endif // SRSUE_NAS_COMMON_H

@ -0,0 +1,61 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
/*
* NAS idle mode Procedures
* As specified in TS 23.122 version 14.5.0
*/
#ifndef SRSUE_NAS_IDLE_PROCEDURES_H
#define SRSUE_NAS_IDLE_PROCEDURES_H
#include "srslte/common/stack_procedure.h"
#include "srsue/hdr/stack/upper/nas.h"
namespace srsue {
class nas::plmn_search_proc
{
public:
struct plmn_search_complete_t {
rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS];
int nof_plmns;
plmn_search_complete_t(const rrc_interface_nas::found_plmn_t* plmns_, int nof_plmns_) : nof_plmns(nof_plmns_)
{
if (nof_plmns > 0) {
std::copy(&plmns_[0], &plmns_[nof_plmns], found_plmns);
}
}
};
explicit plmn_search_proc(nas* nas_ptr_) : nas_ptr(nas_ptr_) {}
srslte::proc_outcome_t init();
srslte::proc_outcome_t step();
void then(const srslte::proc_state_t& result);
srslte::proc_outcome_t react(const plmn_search_complete_t& t);
static const char* name() { return "NAS PLMN Search"; }
private:
nas* nas_ptr;
};
} // namespace srsue
#endif // SRSUE_NAS_PLMN_SELECT_H

@ -27,8 +27,8 @@
namespace srsue {
struct nas_metrics_t {
uint32_t nof_active_eps_bearer;
emm_state_t state;
uint32_t nof_active_eps_bearer;
emm_state_t::state_t state;
};
} // namespace srsue

@ -166,8 +166,7 @@ void ue_stack_lte::stop_impl()
bool ue_stack_lte::switch_on()
{
if (running) {
ue_task_queue.try_push([this]() { nas.start_attach_proc(nullptr, srslte::establishment_cause_t::mo_sig); });
return true;
ue_task_queue.try_push([this]() { nas.switch_on(); });
}
return false;
}
@ -175,7 +174,7 @@ bool ue_stack_lte::switch_on()
bool ue_stack_lte::switch_off()
{
// generate detach request with switch-off flag
nas.detach_request(true);
nas.switch_off();
// wait for max. 5s for it to be sent (according to TS 24.301 Sec 25.5.2.2)
int cnt = 0, timeout_ms = 5000;
@ -195,14 +194,22 @@ bool ue_stack_lte::enable_data()
{
// perform attach request
srslte::console("Turning off airplane mode.\n");
return switch_on();
return nas.enable_data();
}
bool ue_stack_lte::disable_data()
{
// generate detach request
srslte::console("Turning on airplane mode.\n");
return nas.detach_request(false);
return nas.disable_data();
}
bool ue_stack_lte::start_service_request()
{
if (running) {
ue_task_queue.try_push([this]() { nas.start_service_request(srslte::establishment_cause_t::mo_data); });
}
return true;
}
bool ue_stack_lte::get_metrics(stack_metrics_t* metrics)
@ -218,7 +225,7 @@ bool ue_stack_lte::get_metrics(stack_metrics_t* metrics)
});
// wait for result
*metrics = pending_stack_metrics.wait_pop();
return (metrics->nas.state == EMM_STATE_REGISTERED && metrics->rrc.state == RRC_STATE_CONNECTED);
return (metrics->nas.state == emm_state_t::state_t::registered && metrics->rrc.state == RRC_STATE_CONNECTED);
}
void ue_stack_lte::run_thread()
@ -251,6 +258,15 @@ void ue_stack_lte::write_sdu(uint32_t lcid, srslte::unique_byte_buffer_t sdu)
}
}
/**
* Check whether nas is attached
* @return bool wether NAS is in EMM_REGISTERED
*/
bool ue_stack_lte::is_attached()
{
return nas.is_attached();
}
/********************
* PHY Interface
*******************/

@ -18,7 +18,7 @@
# and at http://www.gnu.org/licenses/.
#
set(SOURCES gw.cc nas.cc usim_base.cc usim.cc tft_packet_filter.cc)
set(SOURCES nas.cc nas_idle_procedures.cc gw.cc usim_base.cc usim.cc tft_packet_filter.cc)
if(HAVE_PCSC)
list(APPEND SOURCES "pcsc_usim.cc")
@ -28,4 +28,4 @@ add_library(srsue_upper STATIC ${SOURCES})
if(HAVE_PCSC)
target_link_libraries(srsue_upper ${PCSCLITE_LIBRARY})
endif(HAVE_PCSC)
endif(HAVE_PCSC)

@ -221,13 +221,14 @@ void gw::run_thread()
return;
}
const static uint32_t ATTACH_WAIT_TOUT = 40; // 4 sec
uint32_t attach_wait = 0;
const static uint32_t ATTACH_WAIT_TOUT = 40, SERVICE_WAIT_TOUT = 40; // 4 sec
uint32_t attach_wait = 0, service_wait = 0;
log.info("GW IP packet receiver thread run_enable\n");
running = true;
while (run_enable) {
// Read packet from TUN
if (SRSLTE_MAX_BUFFER_SIZE_BYTES - SRSLTE_BUFFER_HEADER_OFFSET > idx) {
N_bytes = read(tun_fd, &pdu->msg[idx], SRSLTE_MAX_BUFFER_SIZE_BYTES - SRSLTE_BUFFER_HEADER_OFFSET - idx);
} else {
@ -236,70 +237,81 @@ void gw::run_thread()
break;
}
log.debug("Read %d bytes from TUN fd=%d, idx=%d\n", N_bytes, tun_fd, idx);
if (N_bytes > 0) {
struct iphdr* ip_pkt = (struct iphdr*)pdu->msg;
struct ipv6hdr* ip6_pkt = (struct ipv6hdr*)pdu->msg;
uint16_t pkt_len = 0;
pdu->N_bytes = idx + N_bytes;
if (ip_pkt->version == 4 || ip_pkt->version == 6) {
if (ip_pkt->version == 4) {
pkt_len = ntohs(ip_pkt->tot_len);
} else if (ip_pkt->version == 6) {
pkt_len = ntohs(ip6_pkt->payload_len) + 40;
} else {
log.error_hex(pdu->msg, pdu->N_bytes, "Unsupported IP version. Dropping packet.\n");
continue;
if (N_bytes <= 0) {
log.error("Failed to read from TUN interface - gw receive thread exiting.\n");
srslte::console("Failed to read from TUN interface - gw receive thread exiting.\n");
break;
}
// Check if IP version makes sense and get packtet length
struct iphdr* ip_pkt = (struct iphdr*)pdu->msg;
struct ipv6hdr* ip6_pkt = (struct ipv6hdr*)pdu->msg;
uint16_t pkt_len = 0;
pdu->N_bytes = idx + N_bytes;
if (ip_pkt->version == 4) {
pkt_len = ntohs(ip_pkt->tot_len);
} else if (ip_pkt->version == 6) {
pkt_len = ntohs(ip6_pkt->payload_len) + 40;
} else {
log.error_hex(pdu->msg, pdu->N_bytes, "Unsupported IP version. Dropping packet.\n");
continue;
}
log.debug("IPv%d packet total length: %d Bytes\n", ip_pkt->version, pkt_len);
// Check if entire packet was received
if (pkt_len == pdu->N_bytes) {
log.info_hex(pdu->msg, pdu->N_bytes, "TX PDU");
// Make sure UE is attached
while (run_enable && !stack->is_attached() && attach_wait < ATTACH_WAIT_TOUT) {
if (!attach_wait) {
log.info("UE is not attached, waiting for NAS attach (%d/%d)\n", attach_wait, ATTACH_WAIT_TOUT);
}
log.debug("IPv%d packet total length: %d Bytes\n", ip_pkt->version, pkt_len);
// Check if entire packet was received
if (pkt_len == pdu->N_bytes) {
log.info_hex(pdu->msg, pdu->N_bytes, "TX PDU");
while (run_enable && !stack->is_lcid_enabled(default_lcid) && attach_wait < ATTACH_WAIT_TOUT) {
if (!attach_wait) {
log.info(
"LCID=%d not active, requesting NAS attach (%d/%d)\n", default_lcid, attach_wait, ATTACH_WAIT_TOUT);
if (not stack->switch_on()) {
log.warning("Could not re-establish the connection\n");
}
}
usleep(100000);
attach_wait++;
}
usleep(100000);
attach_wait++;
}
attach_wait = 0;
attach_wait = 0;
// If we are still not attached by this stage, drop packet
if (run_enable && !stack->is_attached()) {
continue;
}
if (!run_enable) {
break;
}
// Wait for service request if necessary
while (run_enable && !stack->is_lcid_enabled(default_lcid) && service_wait < SERVICE_WAIT_TOUT) {
if (!service_wait) {
log.info("UE does not have service, waiting for NAS service request (%d/%d)\n", service_wait, SERVICE_WAIT_TOUT);
stack->start_service_request();
}
usleep(100000);
service_wait++;
}
service_wait = 0;
// Quit before writing packet if necessary
if (!run_enable) {
break;
}
uint8_t lcid = tft_matcher.check_tft_filter_match(pdu);
// Send PDU directly to PDCP
if (stack->is_lcid_enabled(lcid)) {
pdu->set_timestamp();
ul_tput_bytes += pdu->N_bytes;
stack->write_sdu(lcid, std::move(pdu));
do {
pdu = srslte::allocate_unique_buffer(*pool);
if (!pdu) {
log.error("Fatal Error: Couldn't allocate PDU in run_thread().\n");
usleep(100000);
}
} while (!pdu);
idx = 0;
uint8_t lcid = tft_matcher.check_tft_filter_match(pdu);
// Send PDU directly to PDCP
if (stack->is_lcid_enabled(lcid)) {
pdu->set_timestamp();
ul_tput_bytes += pdu->N_bytes;
stack->write_sdu(lcid, std::move(pdu));
do {
pdu = srslte::allocate_unique_buffer(*pool);
if (!pdu) {
log.error("Fatal Error: Couldn't allocate PDU in run_thread().\n");
usleep(100000);
}
} else {
idx += N_bytes;
log.debug(
"Entire packet not read from socket. Total Length %d, N_Bytes %d.\n", ip_pkt->tot_len, pdu->N_bytes);
}
} else {
log.error("IP Version not handled. Version %d\n", ip_pkt->version);
} while (!pdu);
idx = 0;
}
} else {
log.error("Failed to read from TUN interface - gw receive thread exiting.\n");
srslte::console("Failed to read from TUN interface - gw receive thread exiting.\n");
break;
idx += N_bytes;
log.debug("Entire packet not read from socket. Total Length %d, N_Bytes %d.\n", ip_pkt->tot_len, pdu->N_bytes);
}
}
running = false;

@ -19,7 +19,6 @@
*
*/
#include "srsue/hdr/stack/upper/nas.h"
#include "srslte/common/bcd_helpers.h"
#include "srslte/common/security.h"
#include "srslte/common/string_helpers.h"
@ -35,202 +34,12 @@
#include "srslte/common/logmap.h"
#include "srslte/common/security.h"
#include "srsue/hdr/stack/upper/nas.h"
#include "srsue/hdr/stack/upper/nas_idle_procedures.h"
using namespace srslte;
#define ProcError(fmt, ...) nas_ptr->nas_log->error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define ProcWarning(fmt, ...) nas_ptr->nas_log->warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define ProcInfo(fmt, ...) nas_ptr->nas_log->info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
namespace srsue {
using srslte::proc_outcome_t;
proc_outcome_t nas::plmn_search_proc::init()
{
// start RRC
state = state_t::plmn_search;
if (not nas_ptr->rrc->plmn_search()) {
ProcError("ProcError while searching for PLMNs\n");
return proc_outcome_t::error;
}
ProcInfo("Starting...\n");
return proc_outcome_t::yield;
}
proc_outcome_t nas::plmn_search_proc::step()
{
return proc_outcome_t::yield;
}
void nas::plmn_search_proc::then(const srslte::proc_state_t& result)
{
ProcInfo("Completed with %s\n", result.is_success() ? "success" : "failure");
if (result.is_error()) {
nas_ptr->enter_emm_deregistered();
}
}
proc_outcome_t nas::plmn_search_proc::react(const rrc_connect_proc::rrc_connect_complete_ev& t)
{
if (state != state_t::rrc_connect) {
// not expecting a rrc connection result
ProcWarning("Received unexpected RRC Connection Result event\n");
return proc_outcome_t::yield;
}
return t.is_success() ? proc_outcome_t::success : proc_outcome_t::error;
}
proc_outcome_t nas::plmn_search_proc::react(const plmn_search_complete_t& t)
{
if (state != state_t::plmn_search) {
ProcWarning("PLMN Search Complete was received but PLMN Search is not running.\n");
return proc_outcome_t::yield; // ignore
}
// check whether the state hasn't changed
if (nas_ptr->state != EMM_STATE_DEREGISTERED or nas_ptr->plmn_is_selected) {
ProcError("ProcError while searching for PLMNs\n");
return proc_outcome_t::error;
}
if (t.nof_plmns < 0) {
ProcError("Error while searching for PLMNs\n");
return proc_outcome_t::error;
}
if (t.nof_plmns == 0) {
ProcWarning("Did not find any PLMN in the set of frequencies.\n");
return proc_outcome_t::error;
}
// Save PLMNs
nas_ptr->known_plmns.clear();
for (int i = 0; i < t.nof_plmns; i++) {
nas_ptr->known_plmns.push_back(t.found_plmns[i].plmn_id);
nas_ptr->nas_log->info(
"Found PLMN: Id=%s, TAC=%d\n", t.found_plmns[i].plmn_id.to_string().c_str(), t.found_plmns[i].tac);
srslte::console(
"Found PLMN: Id=%s, TAC=%d\n", t.found_plmns[i].plmn_id.to_string().c_str(), t.found_plmns[i].tac);
}
nas_ptr->select_plmn();
// Select PLMN in request establishment of RRC connection
if (not nas_ptr->plmn_is_selected) {
ProcError("PLMN is not selected because no suitable PLMN was found\n");
return proc_outcome_t::error;
}
nas_ptr->rrc->plmn_select(nas_ptr->current_plmn);
state = state_t::rrc_connect;
if (not nas_ptr->rrc_connector.launch(srslte::establishment_cause_t::mo_sig, nullptr)) {
ProcError("Unable to initiate RRC connection.\n");
return proc_outcome_t::error;
}
nas_ptr->callbacks.add_proc(nas_ptr->rrc_connector);
return proc_outcome_t::yield;
}
nas::rrc_connect_proc::rrc_connect_proc(nas* nas_ptr_) : nas_ptr(nas_ptr_)
{
timeout_timer = nas_ptr->task_sched.get_unique_timer();
timeout_timer.set(attach_timeout_ms,
[this](uint32_t tid) { nas_ptr->rrc_connector.trigger(nas::rrc_connect_proc::attach_timeout{}); });
}
proc_outcome_t nas::rrc_connect_proc::init(srslte::establishment_cause_t cause_, srslte::unique_byte_buffer_t pdu)
{
if (nas_ptr->rrc->is_connected()) {
ProcInfo("Stopping. Reason: Already connected\n");
return proc_outcome_t::success;
}
if (pdu == nullptr) {
// Generate service request or attach request message
pdu = srslte::allocate_unique_buffer(*nas_ptr->pool, true);
if (!pdu) {
ProcError("Fatal Error: Couldn't allocate PDU.\n");
return proc_outcome_t::error;
}
if (nas_ptr->state == EMM_STATE_REGISTERED) {
nas_ptr->gen_service_request(pdu);
} else {
nas_ptr->gen_attach_request(pdu);
}
}
// Provide UE-Identity to RRC if have one
if (nas_ptr->have_guti) {
srslte::s_tmsi_t s_tmsi;
s_tmsi.mmec = nas_ptr->ctxt.guti.mme_code;
s_tmsi.m_tmsi = nas_ptr->ctxt.guti.m_tmsi;
nas_ptr->rrc->set_ue_identity(s_tmsi);
}
ProcInfo("Starting...\n");
state = state_t::conn_req;
if (not nas_ptr->rrc->connection_request(cause_, std::move(pdu))) {
ProcError("Failed to initiate a connection request procedure\n");
return proc_outcome_t::error;
}
return proc_outcome_t::yield;
}
proc_outcome_t nas::rrc_connect_proc::step()
{
if (state != state_t::wait_attach) {
return proc_outcome_t::yield;
}
// Wait until attachment. If doing a service request is already attached
if (not nas_ptr->running) {
ProcError("NAS stopped running\n");
return proc_outcome_t::error;
} else if (not nas_ptr->rrc->is_connected()) {
ProcError("Was disconnected while attaching\n");
return proc_outcome_t::error;
} else if (nas_ptr->state == EMM_STATE_REGISTERED) {
ProcInfo("Success: EMM Registered correctly.\n");
return proc_outcome_t::success;
}
// still expecting attach finish
return proc_outcome_t::yield;
}
srslte::proc_outcome_t nas::rrc_connect_proc::react(attach_timeout event)
{
if (state != state_t::wait_attach) {
return proc_outcome_t::yield;
}
if (nas_ptr->state == EMM_STATE_DEREGISTERED) {
ProcError("Timeout or received attach reject while trying to attach\n");
}
return proc_outcome_t::error;
}
void nas::rrc_connect_proc::then(const srslte::proc_state_t& result)
{
timeout_timer.stop();
nas_ptr->plmn_searcher.trigger(result);
}
proc_outcome_t nas::rrc_connect_proc::react(nas::rrc_connect_proc::connection_request_completed_t event)
{
if (state == state_t::conn_req and event.outcome) {
ProcInfo("Connection established correctly. Waiting for Attach\n");
// Wait until attachment. If doing a service request is already attached
state = state_t::wait_attach;
timeout_timer.run();
return proc_outcome_t::yield;
} else {
ProcError("Could not establish RRC connection\n");
return proc_outcome_t::error;
}
}
/*********************************************************************
* NAS
********************************************************************/
@ -238,7 +47,6 @@ proc_outcome_t nas::rrc_connect_proc::react(nas::rrc_connect_proc::connection_re
nas::nas(srslte::task_sched_handle task_sched_) :
pool(byte_buffer_pool::get_instance()),
plmn_searcher(this),
rrc_connector(this),
task_sched(task_sched_),
t3402(task_sched_.get_unique_timer()),
t3410(task_sched_.get_unique_timer()),
@ -254,7 +62,6 @@ void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_
usim = usim_;
rrc = rrc_;
gw = gw_;
enter_state(EMM_STATE_DEREGISTERED);
if (!usim->get_home_plmn_id(&home_plmn)) {
nas_log->error("Getting Home PLMN Id from USIM. Defaulting to 001-01\n");
@ -311,6 +118,8 @@ void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_
running = true;
}
nas::~nas() {}
void nas::stop()
{
running = false;
@ -320,27 +129,86 @@ void nas::stop()
void nas::get_metrics(nas_metrics_t* m)
{
nas_metrics_t metrics = {};
metrics.state = state;
metrics.state = state.get_state();
metrics.nof_active_eps_bearer = eps_bearer.size();
*m = metrics;
}
emm_state_t nas::get_state()
void nas::run_tti()
{
return state;
// Process PLMN selection ongoing procedures
callbacks.run();
// Transmit intiating messages if necessary
switch (state.get_state()) {
case emm_state_t::state_t::deregistered:
// TODO Make sure cell selection is finished after transitioning from another state (if required)
// Make sure the RRC is finished transitioning to RRC Idle
if (reattach_timer.is_running()) {
nas_log->debug("Waiting for reatach timer to expire to attach again.\n");
return;
}
switch (state.get_deregistered_substate()) {
case emm_state_t::deregistered_substate_t::plmn_search:
start_plmn_selection_proc();
break;
case emm_state_t::deregistered_substate_t::normal_service:
case emm_state_t::deregistered_substate_t::attach_needed:
start_attach_request(srslte::establishment_cause_t::mo_data);
break;
case emm_state_t::deregistered_substate_t::attempting_to_attach:
nas_log->debug("Attempting to attach\n");
default:
break;
}
case emm_state_t::state_t::registered:
break;
case emm_state_t::state_t::deregistered_initiated:
nas_log->debug("UE detaching...\n");
break;
default:
break;
}
}
void nas::run_tti()
/*******************************************************************************
* FSM Helperse
******************************************************************************/
void nas::enter_emm_null()
{
callbacks.run();
// Deactivate EPS bearer according to Sec. 5.5.2.2.2
nas_log->debug("Clearing EPS bearer context\n");
eps_bearer.clear();
state.set_null();
}
void nas::enter_emm_deregistered_initiated()
{
// Deactivate EPS bearer according to Sec. 5.5.2.2.2
nas_log->debug("Clearing EPS bearer context\n");
eps_bearer.clear();
state.set_deregistered_initiated();
}
void nas::enter_emm_deregistered(emm_state_t::deregistered_substate_t substate)
{
// TODO Start cell selection.
// Deactivate EPS bearer according to Sec. 5.5.2.2.2
nas_log->debug("Clearing EPS bearer context\n");
eps_bearer.clear();
state.set_deregistered(substate);
}
/*******************************************************************************
* NAS Timers
******************************************************************************/
void nas::timer_expired(uint32_t timeout_id)
{
if (timeout_id == t3402.id()) {
nas_log->info("Timer T3402 expired: trying to attach again\n");
attach_attempt_counter = 0; // Sec. 5.5.1.1
start_attach_proc(nullptr, srslte::establishment_cause_t::mo_sig);
enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search);
} else if (timeout_id == t3410.id()) {
// Section 5.5.1.2.6 case c)
attach_attempt_counter++;
@ -351,8 +219,9 @@ void nas::timer_expired(uint32_t timeout_id)
attach_attempt_counter,
max_attach_attempts);
// start T3411, ToDo: EMM-DEREGISTERED.ATTEMPTING-TO-ATTACH isn't fully implemented yet
// start T3411
t3411.run();
enter_emm_deregistered(emm_state_t::deregistered_substate_t::attempting_to_attach);
} else {
// maximum attach attempts reached
nas_log->info("Timer T3410 expired. Maximum attempts reached. Starting T3402\n");
@ -360,22 +229,11 @@ void nas::timer_expired(uint32_t timeout_id)
reset_security_context();
}
} else if (timeout_id == t3411.id()) {
nas_log->info("Timer T3411 expired: trying to attach again\n");
if (rrc->is_connected()) {
// send attach request through established RRC connection
send_attach_request();
} else {
// start attach procedure
start_attach_proc(nullptr, srslte::establishment_cause_t::mo_sig);
}
} else if (timeout_id == t3421.id()) {
nas_log->info("Timer T3421 expired: entering EMM_STATE_DEREGISTERED\n");
// TODO: TS 24.301 says to resend detach request but doesn't say how often before entering EMM_STATE_DEREGISTERED
// In order to allow reattaching the UE, we switch into EMM_STATE_DEREGISTERED straight
enter_emm_deregistered();
enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search);
} else if (timeout_id == reattach_timer.id()) {
nas_log->info("Reattach timer expired: trying to attach again\n");
start_attach_proc(nullptr, srslte::establishment_cause_t::mo_sig);
start_attach_request(srslte::establishment_cause_t::mo_sig);
} else if (timeout_id == airplane_mode_sim_timer.id()) {
if (airplane_mode_state == DISABLED) {
// Enabling air-plane mode
@ -388,7 +246,7 @@ void nas::timer_expired(uint32_t timeout_id)
}
} else if (airplane_mode_state == ENABLED) {
// Disabling airplane mode again
start_attach_proc(nullptr, srslte::establishment_cause_t::mo_sig);
start_attach_request(srslte::establishment_cause_t::mo_sig);
airplane_mode_state = DISABLED;
if (cfg.sim.airplane_t_off_ms > 0) {
@ -400,157 +258,178 @@ void nas::timer_expired(uint32_t timeout_id)
nas_log->error("Timeout from unknown timer id %d\n", timeout_id);
}
}
/*******************************************************************************
* UE Stack and RRC common Interface
******************************************************************************/
bool nas::is_attached()
{
return state.get_state() == emm_state_t::state_t::registered;
}
/*******************************************************************************
* UE interface
* UE Stack Interface
******************************************************************************/
bool nas::switch_on()
{
state.set_deregistered(emm_state_t::deregistered_substate_t::plmn_search);
return true;
}
bool nas::switch_off()
{
detach_request(true);
return true;
}
bool nas::enable_data()
{
return switch_on();
}
bool nas::disable_data()
{
detach_request(false);
return true;
}
/** Blocking function to Attach to the network and establish RRC connection if not established.
/**
* Non-blocking function to Attach to the network and establish RRC connection if not established.
* The function returns true if the UE could attach correctly or false in case of error or timeout during attachment.
*
*/
void nas::start_attach_proc(srslte::proc_state_t* result, srslte::establishment_cause_t cause_)
void nas::start_attach_request(srslte::establishment_cause_t cause_)
{
if (rrc->is_connected()) {
nas_log->info("RRC is connected. Aborting attach reuqest %s.\n", to_string(cause_).c_str());
return;
}
nas_log->info("Attach Request with cause %s.\n", to_string(cause_).c_str());
switch (state) {
case EMM_STATE_DEREGISTERED:
// Search PLMN is not selected
if (!plmn_is_selected) {
nas_log->info("No PLMN selected. Starting PLMN Search...\n");
if (not plmn_searcher.launch()) {
if (result != nullptr) {
result->set_error();
}
return;
}
plmn_searcher.then([this, result, cause_](const proc_state_t& res) {
nas_log->info("Attach Request from PLMN Search %s\n", res.is_success() ? "finished successfully" : "failed");
if (result != nullptr) {
*result = res;
}
if (!res.is_success()) {
// try again ..
task_sched.defer_callback(reattach_timer_duration_ms, [&]() { start_attach_proc(nullptr, cause_); });
}
});
} else {
nas_log->info("PLMN selected in state %s\n", emm_state_text[state]);
if (not rrc_connector.launch(cause_, nullptr)) {
nas_log->error("Cannot initiate concurrent rrc connection procedures\n");
if (result != nullptr) {
result->set_error();
}
return;
}
rrc_connector.then([this, result](const proc_state_t& res) {
if (res.is_success()) {
nas_log->info("NAS attached successfully\n");
} else {
nas_log->error("Could not attach from attach_request\n");
}
if (result != nullptr) {
*result = res;
}
});
callbacks.add_proc(rrc_connector);
}
break;
case EMM_STATE_REGISTERED:
if (rrc->is_connected()) {
nas_log->info("NAS is already registered and RRC connected\n");
if (result != nullptr) {
result->set_val();
}
} else {
nas_log->info("NAS is already registered but RRC disconnected. Connecting now...\n");
if (not rrc_connector.launch(cause_, nullptr)) {
nas_log->error("Cannot initiate concurrent rrc connection procedures\n");
if (result != nullptr) {
result->set_error();
}
return;
}
rrc_connector.then([this, result](const proc_state_t& res) {
if (res.is_success()) {
nas_log->info("NAS attached successfully\n");
} else {
nas_log->error("Could not attach from attach_request\n");
}
if (result != nullptr) {
*result = res;
}
});
callbacks.add_proc(rrc_connector);
}
break;
default:
nas_log->info("Attach request ignored. State = %s\n", emm_state_text[state]);
if (result != nullptr) {
result->set_error();
}
if (state.get_state() != emm_state_t::state_t::deregistered) {
nas_log->info("NAS in invalid state for Attach Request\n");
nas_log->info("Attach request ignored. State = %s\n", state.get_full_state_text().c_str());
return;
}
// start T3410
nas_log->debug("Starting T3410\n");
t3410.run();
// stop T3411
if (t3411.is_running()) {
t3411.stop();
}
// Todo: stop T3402
// Start attach request
unique_byte_buffer_t msg = srslte::allocate_unique_buffer(*pool, true);
gen_attach_request(msg);
if (!rrc->is_connected()) {
nas_log->debug("Initiating RRC Connection\n");
if (not rrc->connection_request(cause_, std::move(msg))) {
nas_log->error("Error starting RRC connection");
return;
}
}
state.set_registered_initiated();
}
void nas::plmn_search_completed(const rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS],
int nof_plmns)
/**
* Non-blocking function to start the Service to the network .
* The function returns true if the UE could attach correctly or false in case of error or timeout during attachment.
*
*/
void nas::start_service_request(srslte::establishment_cause_t cause_)
{
plmn_searcher.trigger(plmn_search_proc::plmn_search_complete_t(found_plmns, nof_plmns));
nas_log->info("Service Request with cause %s.\n", to_string(cause_).c_str());
srslte::console("Service Request with cause %s.\n", to_string(cause_).c_str());
if (state.get_state() != emm_state_t::state_t::registered) {
nas_log->info("NAS in invalid state for Service Request\n");
nas_log->info("Service request ignored. State = %s\n", state.get_full_state_text().c_str());
return;
}
if (rrc->is_connected()) {
nas_log->info("NAS is already registered and RRC connected\n");
nas_log->info("Service request ignored. State = %s\n", state.get_full_state_text().c_str());
return;
}
nas_log->info("NAS is already registered but RRC disconnected. Connecting now...\n");
// Start service request
unique_byte_buffer_t msg = srslte::allocate_unique_buffer(*pool, true);
gen_service_request(msg);
if (not rrc->connection_request(cause_, std::move(msg))) {
nas_log->error("Error starting RRC connection");
return;
}
state.set_service_request_initiated();
}
/**
* Non-blocking function to send the detach request to the network .
* The function returns true if the UE could attach correctly or false in case of error or timeout during attachment.
*
*/
bool nas::detach_request(const bool switch_off)
{
switch (state) {
case EMM_STATE_REGISTERED:
switch (state.get_state()) {
case emm_state_t::state_t::deregistered:
case emm_state_t::state_t::deregistered_initiated: // Fall-through
// do nothing ..
break;
case emm_state_t::state_t::service_request_initiated:
case emm_state_t::state_t::registered_initiated:
case emm_state_t::state_t::registered: // Fall-through
// send detach request
send_detach_request(switch_off);
break;
case EMM_STATE_DEREGISTERED:
case EMM_STATE_DEREGISTERED_INITIATED:
default:
nas_log->debug("Received request to detach in state %s\n", emm_state_text[state]);
nas_log->debug("Received request to detach in state %s\n", state.get_full_state_text().c_str());
break;
}
if (switch_off) {
enter_emm_null();
}
return false;
}
void nas::enter_emm_deregistered()
/*******************************************************************************
* RRC Interface
******************************************************************************/
// Signal from RRC that connection request proc completed
bool nas::connection_request_completed(bool outcome)
{
// Deactivate EPS bearer according to Sec. 5.5.2.2.2
nas_log->debug("Clearing EPS bearer context\n");
eps_bearer.clear();
plmn_is_selected = false;
enter_state(EMM_STATE_DEREGISTERED);
nas_log->debug("RRC connection request completed. NAS State %s.\n", state.get_full_state_text().c_str());
if (state.get_state() == emm_state_t::state_t::service_request_initiated) {
srslte::console("Service Request Finished.\n");
rrc->paging_completed(true);
state.set_registered(emm_state_t::registered_substate_t::normal_service);
}
return true;
}
void nas::enter_state(emm_state_t state_)
void nas::plmn_search_completed(const rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS],
int nof_plmns)
{
state = state_;
nas_log->info("New state %s\n", emm_state_text[state]);
plmn_searcher.trigger(plmn_search_proc::plmn_search_complete_t(found_plmns, nof_plmns));
}
void nas::left_rrc_connected() {}
bool nas::is_attached()
void nas::left_rrc_connected()
{
return state == EMM_STATE_REGISTERED;
nas_log->debug("RRC no longer connected. NAS State %s.\n", state.get_full_state_text().c_str());
}
bool nas::paging(s_tmsi_t* ue_identity)
{
if (state == EMM_STATE_REGISTERED) {
if (state.get_state() == emm_state_t::state_t::registered) {
nas_log->info("Received paging: requesting RRC connection establishment\n");
if (not rrc_connector.launch(srslte::establishment_cause_t::mt_access, nullptr)) {
nas_log->error("Could not launch RRC Connect()\n");
return false;
}
// once completed, call paging complete
rrc_connector.then([this](proc_state_t outcome) { rrc->paging_completed(outcome.is_success()); });
callbacks.add_proc(rrc_connector);
start_service_request(srslte::establishment_cause_t::mt_access);
} else {
nas_log->warning("Received paging while in state %s\n", emm_state_text[state]);
nas_log->warning("Received paging while in state %s\n", state.get_full_state_text().c_str());
return false;
}
return true;
@ -561,46 +440,6 @@ void nas::set_barring(barring_t barring)
current_barring = barring;
}
// Signal from RRC that connection request proc completed
bool nas::connection_request_completed(bool outcome)
{
rrc_connector.trigger(rrc_connect_proc::connection_request_completed_t{outcome});
return true;
}
void nas::select_plmn()
{
plmn_is_selected = false;
// First find if Home PLMN is available
for (const srslte::plmn_id_t& known_plmn : known_plmns) {
if (known_plmn == home_plmn) {
nas_log->info("Selecting Home PLMN Id=%s\n", known_plmn.to_string().c_str());
plmn_is_selected = true;
current_plmn = known_plmn;
return;
}
}
// If not, select the first available PLMN
if (not known_plmns.empty()) {
nas_log->info("Could not find Home PLMN Id=%s, trying to connect to PLMN Id=%s\n",
home_plmn.to_string().c_str(),
known_plmns[0].to_string().c_str());
srslte::console("Could not find Home PLMN Id=%s, trying to connect to PLMN Id=%s\n",
home_plmn.to_string().c_str(),
known_plmns[0].to_string().c_str());
plmn_is_selected = true;
current_plmn = known_plmns[0];
}
// reset attach attempt counter (Sec. 5.2.2.3.4)
if (plmn_is_selected) {
attach_attempt_counter = 0;
}
}
void nas::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu)
{
uint8 pd = 0;
@ -777,12 +616,57 @@ bool nas::get_ipv6_addr(uint8_t* ipv6_addr)
}
/*******************************************************************************
PCAP
*******************************************************************************/
* PLMN selection helpers
******************************************************************************/
void nas::start_plmn_selection_proc()
{
nas_log->debug("Attempting to select PLMN\n");
if (plmn_searcher.is_idle()) {
nas_log->info("No PLMN selected. Starting PLMN Selection...\n");
if (not plmn_searcher.launch()) {
nas_log->error("Error starting PLMN selection");
return;
}
}
}
void nas::start_pcap(srslte::nas_pcap* pcap_)
// Select PLMN from list of known PLMNs.
// TODO check whether PLMN or Tracking Area of the selected cell is in forbiden list.
void nas::select_plmn()
{
pcap = pcap_;
nas_log->debug("Selecting PLMN from list of known PLMNs.\n");
// check whether the state hasn't changed
if (state.get_state() != emm_state_t::state_t::deregistered and
state.get_deregistered_substate() != emm_state_t::deregistered_substate_t::plmn_search) {
nas_log->error("Selecting PLMN when in incorrect EMM State\n");
return;
}
// First find if Home PLMN is available
for (const srslte::plmn_id_t& known_plmn : known_plmns) {
if (known_plmn == home_plmn) {
nas_log->info("Selecting Home PLMN Id=%s\n", known_plmn.to_string().c_str());
current_plmn = known_plmn;
state.set_deregistered(emm_state_t::deregistered_substate_t::normal_service);
return;
}
}
// If not, select the first available PLMN
if (not known_plmns.empty()) {
std::string debug_str = "Could not find Home PLMN Id=" + home_plmn.to_string() +
", trying to connect to PLMN Id=" + known_plmns[0].to_string() + "\n";
nas_log->info("%s", debug_str.c_str());
srslte::console(debug_str.c_str());
current_plmn = known_plmns[0];
state.set_deregistered(emm_state_t::deregistered_substate_t::normal_service);
}
// reset attach attempt counter (Sec. 5.2.2.3.4)
if (state.get_deregistered_substate() == emm_state_t::deregistered_substate_t::normal_service) {
attach_attempt_counter = 0;
}
}
/*******************************************************************************
@ -1274,9 +1158,8 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu)
bearer.eps_bearer_id = act_def_eps_bearer_context_req.eps_bearer_id;
if (eps_bearer.insert(eps_bearer_map_pair_t(bearer.eps_bearer_id, bearer)).second) {
// bearer added successfully
enter_state(EMM_STATE_REGISTERED);
attach_attempt_counter = 0; // reset according to 5.5.1.1
state.set_registered(emm_state_t::registered_substate_t::normal_service);
// send attach complete
send_attach_complete(transaction_id, bearer.eps_bearer_id);
@ -1288,7 +1171,7 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu)
} else {
nas_log->info("Not handling attach type %u\n", attach_accept.eps_attach_result);
enter_emm_deregistered();
enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search);
}
ctxt.rx_count++;
@ -1317,6 +1200,7 @@ void nas::parse_attach_reject(uint32_t lcid, unique_byte_buffer_t pdu)
attach_rej.emm_cause == LIBLTE_MME_EMM_CAUSE_NO_SUITABLE_CELLS_IN_TRACKING_AREA ||
attach_rej.emm_cause == LIBLTE_MME_EMM_CAUSE_NOT_AUTHORIZED_FOR_THIS_CSG) {
attach_attempt_counter = 0;
enter_emm_deregistered(emm_state_t::deregistered_substate_t::attempting_to_attach);
}
// 5.5.1.2.5
@ -1325,11 +1209,17 @@ void nas::parse_attach_reject(uint32_t lcid, unique_byte_buffer_t pdu)
attach_rej.emm_cause == LIBLTE_MME_EMM_CAUSE_REQUESTED_SERVICE_OPTION_NOT_AUTHORIZED) {
// delete security context
reset_security_context();
enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search);
reattach_timer.run();
}
// TODO: handle other relevant reject causes
enter_emm_deregistered();
if (attach_rej.emm_cause == LIBLTE_MME_EMM_CAUSE_ROAMING_NOT_ALLOWED_IN_THIS_TRACKING_AREA ||
attach_rej.emm_cause == LIBLTE_MME_EMM_CAUSE_EPS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN ||
attach_rej.emm_cause == LIBLTE_MME_EMM_CAUSE_NETWORK_FAILURE) {
enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search);
}
}
void nas::parse_authentication_request(uint32_t lcid, unique_byte_buffer_t pdu, const uint8_t sec_hdr_type)
@ -1383,7 +1273,8 @@ void nas::parse_authentication_request(uint32_t lcid, unique_byte_buffer_t pdu,
void nas::parse_authentication_reject(uint32_t lcid, unique_byte_buffer_t pdu)
{
nas_log->warning("Received Authentication Reject\n");
enter_emm_deregistered();
reset_security_context();
enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search);
// TODO: Command RRC to release?
}
@ -1535,7 +1426,7 @@ void nas::parse_service_reject(uint32_t lcid, unique_byte_buffer_t pdu)
// TODO: handle NAS backoff-timers correctly
enter_emm_deregistered();
enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search);
reset_security_context();
// Send attach request after receiving service reject
@ -1574,16 +1465,17 @@ void nas::parse_detach_request(uint32_t lcid, unique_byte_buffer_t pdu)
liblte_mme_unpack_detach_request_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), &detach_request);
ctxt.rx_count++;
nas_log->info("Received detach request (type=%d)\n", detach_request.detach_type.type_of_detach);
nas_log->info("Received detach request (type=%d). NAS State: %s\n",
detach_request.detach_type.type_of_detach,
state.get_full_state_text().c_str());
switch (state) {
case EMM_STATE_DEREGISTERED_INITIATED:
nas_log->info("Received detach from network while performing UE initiated detach. Aborting UE detach.\n");
// intentional fall-through to complete detach procedure
case EMM_STATE_REGISTERED:
switch (state.get_state()) {
case emm_state_t::state_t::service_request_initiated: // intentional fall-through to complete detach procedure
case emm_state_t::state_t::deregistered_initiated: // intentional fall-through to complete detach procedure
case emm_state_t::state_t::registered:
// send accept and leave state
send_detach_accept();
enter_emm_deregistered();
enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search);
// schedule reattach if required
if (detach_request.detach_type.type_of_detach == LIBLTE_MME_TOD_DL_REATTACH_REQUIRED) {
@ -1594,7 +1486,7 @@ void nas::parse_detach_request(uint32_t lcid, unique_byte_buffer_t pdu)
}
break;
default:
nas_log->warning("Received detach request in invalid state (%s)\n", emm_state_text[state]);
nas_log->warning("Received detach request in invalid state (%s)\n", state.get_full_state_text().c_str());
break;
}
}
@ -1864,7 +1756,7 @@ void nas::gen_attach_request(srslte::unique_byte_buffer_t& msg)
}
// start T3410
nas_log->debug("Starting T3410\n");
nas_log->debug("Starting T3410. Timeout in %d ms.\n", t3410.duration());
t3410.run();
}
@ -2025,10 +1917,10 @@ void nas::send_detach_request(bool switch_off)
}
if (switch_off) {
enter_emm_deregistered();
enter_emm_deregistered_initiated();
} else {
// we are expecting a response from the core
enter_state(EMM_STATE_DEREGISTERED_INITIATED);
state.set_deregistered_initiated();
// start T3421
nas_log->info("Starting T3421\n");
@ -2038,10 +1930,9 @@ void nas::send_detach_request(bool switch_off)
if (rrc->is_connected()) {
rrc->write_sdu(std::move(pdu));
} else {
if (not rrc_connector.launch(establishment_cause_t::mo_sig, std::move(pdu))) {
nas_log->error("Failed to initiate RRC Connection Request\n");
if (not rrc->connection_request(srslte::establishment_cause_t::mo_sig, std::move(pdu))) {
nas_log->error("Error starting RRC connection");
}
callbacks.add_proc(rrc_connector);
}
ctxt.tx_count++;

@ -0,0 +1,104 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srsue/hdr/stack/upper/nas_idle_procedures.h"
using namespace srslte;
#define ProcError(fmt, ...) nas_ptr->nas_log->error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define ProcWarning(fmt, ...) nas_ptr->nas_log->warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define ProcInfo(fmt, ...) nas_ptr->nas_log->info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
namespace srsue {
using srslte::proc_outcome_t;
/*
* PLMN Selection Procedures
* As specified in TS 23.122 version 14.5.0
*/
proc_outcome_t nas::plmn_search_proc::init()
{
// start RRC PLMN selection
if (not nas_ptr->rrc->plmn_search()) {
ProcError("ProcError while searching for PLMNs\n");
return proc_outcome_t::error;
}
ProcInfo("Starting...\n");
return proc_outcome_t::yield;
}
proc_outcome_t nas::plmn_search_proc::step()
{
return proc_outcome_t::yield;
}
void nas::plmn_search_proc::then(const srslte::proc_state_t& result)
{
ProcInfo("Completed with %s\n", result.is_success() ? "success" : "failure");
if (result.is_error()) {
nas_ptr->enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search);
}
}
proc_outcome_t nas::plmn_search_proc::react(const plmn_search_complete_t& t)
{
// check whether the NAS state hasn't changed
if (nas_ptr->state.get_state() != emm_state_t::state_t::deregistered and
nas_ptr->state.get_deregistered_substate() != emm_state_t::deregistered_substate_t::plmn_search) {
ProcError("ProcError while searching for PLMNs\n");
return proc_outcome_t::error;
}
if (t.nof_plmns < 0) {
ProcError("Error while searching for PLMNs\n");
return proc_outcome_t::error;
}
if (t.nof_plmns == 0) {
ProcWarning("Did not find any PLMN in the set of frequencies.\n");
return proc_outcome_t::error;
}
// Save PLMNs
nas_ptr->known_plmns.clear();
for (int i = 0; i < t.nof_plmns; i++) {
nas_ptr->known_plmns.push_back(t.found_plmns[i].plmn_id);
nas_ptr->nas_log->info(
"Found PLMN: Id=%s, TAC=%d\n", t.found_plmns[i].plmn_id.to_string().c_str(), t.found_plmns[i].tac);
srslte::console("Found PLMN: Id=%s, TAC=%d\n", t.found_plmns[i].plmn_id.to_string().c_str(), t.found_plmns[i].tac);
}
nas_ptr->select_plmn();
// Select PLMN in request establishment of RRC connection
if (nas_ptr->state.get_state() != emm_state_t::state_t::deregistered and
nas_ptr->state.get_deregistered_substate() != emm_state_t::deregistered_substate_t::normal_service) {
ProcError("PLMN is not selected because no suitable PLMN was found\n");
return proc_outcome_t::error;
}
nas_ptr->rrc->plmn_select(nas_ptr->current_plmn);
return proc_outcome_t::success;
}
} // namespace srsue

@ -154,17 +154,17 @@ public:
nas = nas_;
start(-1);
}
bool switch_on() final
bool switch_on()
{
proc_state_t proc_result;
nas->start_attach_proc(&proc_result, srslte::establishment_cause_t::mo_data);
while (not proc_result.is_complete()) {
usleep(1000);
}
return proc_result.is_success();
return true;
}
void write_sdu(uint32_t lcid, srslte::unique_byte_buffer_t sdu) { pdcp->write_sdu(lcid, std::move(sdu)); }
bool is_lcid_enabled(uint32_t lcid) { return pdcp->is_lcid_enabled(lcid); }
bool is_attached(){return true;}
bool start_service_request(){return true;}
void run_thread()
{
running = true;
@ -179,6 +179,7 @@ public:
running = false;
wait_thread_finish();
}
srslte::log_ref stack_log{"STACK"};
pdcp_interface_gw* pdcp = nullptr;
srsue::nas* nas = nullptr;
bool running = false;
@ -312,9 +313,7 @@ int mme_attach_request_test()
gw.init(gw_args, g_logger, &stack);
stack.init(&nas);
usleep(5000); // Wait for stack to initialize before stoping it.
usleep(1000); // Wait for stack to initialize before stoping it.
// trigger test
stack.switch_on();
stack.stop();
@ -330,7 +329,6 @@ int mme_attach_request_test()
memcpy(tmp->msg, attach_accept_pdu, sizeof(attach_accept_pdu));
tmp->N_bytes = sizeof(attach_accept_pdu);
nas.write_pdu(LCID, std::move(tmp));
nas_metrics_t metrics;
nas.get_metrics(&metrics);
TESTASSERT(metrics.nof_active_eps_bearer == 1);

Loading…
Cancel
Save