Compare commits

...

16 Commits

Author SHA1 Message Date
Vadim Yanitskiy 2df561cfa2 srsue/extnas: write NAS frames to the given PCAP file 2022-01-31 17:18:02 +06:00
Vadim Yanitskiy fbb2ed3aa9 srsue/extnas: implement encryption and signing of NAS messages 2022-01-31 17:18:02 +06:00
Vadim Yanitskiy 19ed00e9bb srsue/extnas: move EEA/EIA API from 'nas' to 'nas_base'
This way 'nas_ext' would also be able to use it.
2022-01-31 17:18:02 +06:00
Vadim Yanitskiy b594b15e79 srsue/extnas: handle (U)SIM GenAuthResp.Request 2022-01-31 17:18:02 +06:00
Vadim Yanitskiy 27344af7f0 srsue/rrctl: make codec::enc_hdr() return proto::msg_hdr 2022-01-31 17:18:02 +06:00
Vadim Yanitskiy 796a6df0df srsue/rrctl: add Security Mode messages for EEA/EIA 2022-01-31 17:18:02 +06:00
Vadim Yanitskiy 510bd4ee02 srsue/rrctl: add (U)SIM specific protocol extensions 2022-01-31 17:18:02 +06:00
Vadim Yanitskiy aeee4da73c srsue/rrctl: reserve a range for RRCTL protocol extensions
The idea is to group protocol extensions and have one message
type (with 4 variations: Req/Ind/Cnf/Err) per group.  Similar
to the stack and heap allocation model in Linux, new entries
shall be added to the 'RRCTL_MsgType' list as follows:

  - regular messages - from bottom to the end,
  - extension groups - from the end towards the bottom

The mask '11xxxx'B gives us 16 unique messages, one is reserved.
2022-01-31 17:18:02 +06:00
Vadim Yanitskiy f68931f6e8 srsue/rrctl: add RFU (Reserved for Further Use) message type 2022-01-31 17:18:01 +06:00
Vadim Yanitskiy 7b38286864 srsue/extnas: implement RRCTL codec and message handling
RRCTL is a simple protocol (inspired by Osmocom's L1CTL) that allows
an external NAS entity to control the RRC layer of srsUE. The most
notable primitives are PLMN search, selection, and PDU transfer.

The protocol assumes traditional master-slave communication, where
one side (an external NAS entity) initiates various processes,
while the other (srsUE) executes them and indicates the outcome.

Each RRCTL message starts with a header that can be defined as follows:

  +-------------------------------+--------------------------+
  | Message type                  | 6 bits                   |
  +-------------------------------+--------------------------+
  | Message sub-type              | 2 bits                   |
  +-------------------------------+--------------------------+
  | Spare (RFU)                   | 8 bits                   |
  +-------------------------------+--------------------------+
  | Payload length                | 2 octets (big endian)    |
  +-------------------------------+--------------------------+
  | Payload (optional)            | (see payload length)     |
  +-------------------------------+--------------------------+

The following message types are defined at the moment:

  - RRCTL_RESET - reset internal state of the external NAS interface
                  (does nothing for now, may be useful in the future);

  - RRCTL_PLMN_SEARCH - initiates PLMN (carrier) search on pre-configured
                        EARFCN (Absolute Radio Freqency Number);

  - RRCTL_PLMN_SELECT - binds the UE to one of the previously detected
                        carriers (see RRCTL_PLMN_SEARCH) defined by a
                        given pair of MCC and MNC;

  - RRCTL_CONN_ESTABLISH - establishes connection to the serving cell
                           (previously selected using RRCTL_PLMN_SELECT)
                           with a given cause and NAS PDU;

  - RRCTL_CONN_RELEASE - releases previously established dedicated connection
                         (currently does nothing because the RRC layer does
                          not expose any API for that);

  - RRCTL_DATA - encapsulates a received (Downlink) or to be transmitted
                 (Uplink) NAS PDU (the former also contains LCID).

Each message type has at least two of the following sub-types:

  - RRCTL_REQ - request (usually comes from an external NAS entity),
                used to initiate some process (e.g. PLMN search);

  - RRCTL_IND - indication that something has happened without a prior
                request (for example, a Downlink NAS PDU was received);

  - RRCTL_CNF - confirmation (positive conslusion) of the requested task;

  - RRCTL_ERR - negative conslusion of the requested task (error).

The protocol definition (enums ans structs) and codec functions are
defined in a separate namespaces: 'rrctl::proto' and 'rrctl::codec'
respectively. The codec functions may throw exceptions of type
'rrctl::codec::error' if something goes wrong.
2022-01-31 17:17:57 +06:00
Vadim Yanitskiy 74b2c5c711 srsue/extnas: add a possibility to enable the external NAS interface
The new configuration section '[extnas]' allows to disable the
built-in NAS implementation, and provide the interface (UNIX
domain socket) to an external entity. The interface itself will
be implemented in the follow up commits.
2020-11-09 06:31:45 +07:00
Vadim Yanitskiy 900ad92912 srsue/extnas: implement a simple UNIX domain socket server
Unfortunately, the existing networking API (common/network_utils.h)
lacks the UNIX domain socket support, and it turned to be easier
to implement a simple, single client server using Boost.Asio.

The server runs in its own thread with a blocking Tx queue, and
calls a user definted callback on receipt of any data from client.
Multiple client connections are not supported and will be rejected.
2020-11-09 06:31:45 +07:00
Vadim Yanitskiy 8bc72d44b9 srsue/extnas: introduce draft implementation of 'nas_ext' class
This commit introduces a skeleton class (child of 'nas_base') for
the upcoming implementation of an external NAS interface, as well
as the implementation-specific configuration container.
2020-11-09 06:31:45 +07:00
Vadim Yanitskiy 2b1ded220e srsue/extnas: derive abstract 'nas_base' class from 'nas'
This is the first step towards the goal of having an external NAS
interface.  The new 'nas_base' class becomes a parent of 'nas',
combining all interfaces and the basic (common) API.

The 'ue_stack_lte' now holds a unique_ptr of type 'srsue::nas_base',
so the underlying NAS implementation (built-in or external) can
be choosen at run-time depending on configuration.

The implementation specific configuration now needs to be passed
to the constructor instead, not to the init() method as was before.
2020-11-09 05:34:03 +07:00
Vadim Yanitskiy d56863ff4e srslte/common: constify the argument of byte_buffer_t::append_bytes()
This function does not modify the input buffer. Let's make it
possible to pass 'const uint8_t *' pointers without loosing
the const qualifier and making GCC unhappy.
2020-11-09 05:34:03 +07:00
Vadim Yanitskiy d4f1310fca srslte/interfaces: mark to_number() of struct plmn_id_t as const
This function does not modify any fields of the structure. Without
the 'const' qualifier it's impossible to call to_number() through
a const pointer of type 'struct plmn_id_t'.
2020-11-09 05:34:03 +07:00
27 changed files with 1612 additions and 95 deletions

View File

@ -12,7 +12,7 @@ jobs:
- name: Build srsLTE on x86 Ubuntu 18.04
run: |
sudo apt update
sudo apt install -y build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libconfig++-dev libsctp-dev colordiff ninja-build valgrind
sudo apt install -y build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libasio-dev libconfig++-dev libsctp-dev colordiff ninja-build valgrind
mkdir build && cd build && cmake -DRF_FOUND=True -GNinja .. && ninja && ctest -T memcheck
x86_ubuntu16_build:
name: Build and test on x86 Ubuntu 16.04
@ -25,7 +25,7 @@ jobs:
- name: Build srsLTE on x86 Ubuntu 16.04
run: |
sudo apt update
sudo apt install -y build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libconfig++-dev libsctp-dev colordiff ninja-build valgrind
sudo apt install -y build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libasio-dev libconfig++-dev libsctp-dev colordiff ninja-build valgrind
mkdir build && cd build && cmake -DRF_FOUND=True -GNinja .. && ninja && ctest -T memcheck
aarch64_ubuntu18_build:
@ -44,5 +44,5 @@ jobs:
run: |
export CTEST_PARALLEL_LEVEL=$(nproc --all)
apt update
apt install -y build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libconfig++-dev libsctp-dev ninja-build
ls -l && pwd && mkdir build && cd build && cmake -DRF_FOUND=True -GNinja .. && ninja
apt install -y build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libasio-dev libconfig++-dev libsctp-dev ninja-build
ls -l && pwd && mkdir build && cd build && cmake -DRF_FOUND=True -GNinja .. && ninja

View File

@ -8,6 +8,7 @@ extraction:
- libmbedtls-dev
- libpcsclite-dev
- libboost-program-options-dev
- libasio-dev
- libconfig++-dev
- libsctp-dev
- libuhd-dev

View File

@ -3,7 +3,7 @@ sudo: required
before_script:
- sudo apt-get -qq update
- sudo apt-get install -qq build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libconfig++-dev libsctp-dev libczmq-dev libpcsclite-dev rapidjson-dev colordiff ninja-build clang-format-8
- sudo apt-get install -qq build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libasio-dev libconfig++-dev libsctp-dev libczmq-dev libpcsclite-dev rapidjson-dev colordiff ninja-build clang-format-8
language: cpp
@ -28,4 +28,4 @@ script:
- cmake -DENABLE_5GNR=True -DENABLE_TTCN3=True -DRF_FOUND=True -G Ninja ..
- ninja
- ninja test
- sudo ninja install
- sudo ninja install

View File

@ -241,6 +241,7 @@ endif(BUILD_STATIC)
set(BOOST_REQUIRED_COMPONENTS
program_options
system
)
if(UNIX AND EXISTS "/usr/lib64")
list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix

View File

@ -119,7 +119,7 @@ Build Instructions
For example, on Ubuntu, one can install the mandatory build dependencies with:
```
sudo apt-get install cmake libfftw3-dev libmbedtls-dev libboost-program-options-dev libconfig++-dev libsctp-dev
sudo apt-get install cmake libfftw3-dev libmbedtls-dev libboost-program-options-dev libasio-dev libconfig++-dev libsctp-dev
```
or on Fedora:
```

View File

@ -1,7 +1,7 @@
SET(CPACK_PACKAGE_DESCRIPTION "srsLTE")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "LTE library for SDR.")
SET(CPACK_PACKAGE_NAME "srslte")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.6), libgcc1 (>= 1:4.1), libboost-dev (>= 1.35)")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.6), libgcc1 (>= 1:4.1), libboost-dev (>= 1.35) libasio-dev")
SET(CPACK_PACKAGE_CONTACT "Ismael Gomez ")
SET(CPACK_PACKAGE_VENDOR "Software Radio Systems Limited")
@ -45,12 +45,12 @@ ENDIF()
########################################################################
# Setup CPack Debian
########################################################################
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-dev")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-dev libasio-dev")
########################################################################
# Setup CPack RPM
########################################################################
SET(CPACK_RPM_PACKAGE_REQUIRES "boost-devel")
SET(CPACK_RPM_PACKAGE_REQUIRES "boost-devel libasio-dev")
########################################################################
# Setup CPack NSIS

1
debian/control vendored
View File

@ -10,6 +10,7 @@ Build-Depends:
libfftw3-dev,
libmbedtls-dev,
libboost-program-options-dev,
libasio-dev,
libconfig++-dev,
libsctp-dev,
libuhd-dev,

View File

@ -174,7 +174,7 @@ public:
#endif
}
void append_bytes(uint8_t* buf, uint32_t size)
void append_bytes(const uint8_t* buf, uint32_t size)
{
memcpy(&msg[N_bytes], buf, size);
N_bytes += size;

View File

@ -66,7 +66,7 @@ struct plmn_id_t {
}
return SRSLTE_SUCCESS;
}
std::pair<uint16_t, uint16_t> to_number()
std::pair<uint16_t, uint16_t> to_number() const
{
uint16_t mcc_num, mnc_num;
srslte::bytes_to_mcc(&mcc[0], &mcc_num);
@ -87,6 +87,34 @@ struct plmn_id_t {
uint8_t* plmn_ptr = (uint8_t*)&s1ap_plmn;
memcpy(&plmn_bytes[0], plmn_ptr + 1, 3);
}
void to_rrctl_bytes(uint8_t *mcc_buf, uint8_t *mnc_buf) const
{
mcc_buf[0] = ((mcc[1] & 0x0f) << 4) | (mcc[0] & 0x0f);
mcc_buf[1] = (0x0f << 4) | (mcc[2] & 0x0f);
mnc_buf[0] = ((mnc[1] & 0x0f) << 4) | (mnc[0] & 0x0f);
if (nof_mnc_digits > 2)
mnc_buf[1] = (0x0f << 4) | (mnc[2] & 0x0f);
else
mnc_buf[1] = 0xff;
}
void from_rrctl_bytes(const uint8_t *mcc_buf, const uint8_t *mnc_buf)
{
mcc[0] = mcc_buf[0] & 0x0f;
mcc[1] = mcc_buf[0] >> 4;
mcc[2] = mcc_buf[1] & 0x0f;
mnc[0] = mnc_buf[0] & 0x0f;
mnc[1] = mnc_buf[0] >> 4;
if (mnc_buf[1] != 0xff) {
nof_mnc_digits = 3;
mnc[2] = mnc_buf[1] & 0x0f;
} else {
nof_mnc_digits = 2;
mnc[2] = 0x00;
}
}
int from_string(const std::string& plmn_str)
{
if (plmn_str.size() < 5 or plmn_str.size() > 6) {

View File

@ -48,6 +48,7 @@ typedef struct {
std::string nas_level;
std::string usim_level;
std::string stack_level;
std::string extif_level;
int mac_hex_limit;
int rlc_hex_limit;
@ -57,6 +58,7 @@ typedef struct {
int nas_hex_limit;
int usim_hex_limit;
int stack_hex_limit;
int extif_hex_limit;
} stack_log_args_t;
typedef struct {
@ -66,6 +68,7 @@ typedef struct {
usim_args_t usim;
rrc_args_t rrc;
std::string ue_category_str;
nas_ext_args_t nas_ext;
nas_args_t nas;
gw_args_t gw;
uint32_t sync_queue_size; // Max allowed difference between PHY and Stack clocks (in TTI)

View File

@ -37,6 +37,7 @@
#include "srslte/upper/pdcp.h"
#include "srslte/upper/rlc.h"
#include "upper/nas.h"
#include "upper/nas_ext.h"
#include "upper/usim.h"
#include "srslte/common/buffer_pool.h"
@ -153,6 +154,7 @@ private:
srslte::log_ref usim_log{"USIM"};
srslte::log_ref nas_log{"NAS"};
srslte::log_ref pool_log{"POOL"};
srslte::log_ref extif_log{"EXTIF"};
// RAT-specific interfaces
phy_interface_stack_lte* phy = nullptr;
@ -174,8 +176,10 @@ private:
srslte::rlc rlc;
srslte::pdcp pdcp;
srsue::rrc rrc;
srsue::nas nas;
std::unique_ptr<usim_base> usim;
// NAS implementation (built-in or external)
std::unique_ptr<srsue::nas_base> nas;
};
} // namespace srsue

View File

@ -38,15 +38,68 @@ using srslte::byte_buffer_t;
namespace srsue {
class nas : public nas_interface_rrc, public nas_interface_ue, public srslte::timer_callback
class nas_base : public nas_interface_rrc, public nas_interface_ue, public srslte::timer_callback
{
public:
explicit nas(srslte::task_sched_handle task_sched_);
virtual ~nas() = default;
void init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& args_);
void stop();
explicit nas_base(srslte::task_sched_handle task_sched_, const char *log_name_);
virtual ~nas_base() = default;
virtual void init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_) = 0;
virtual void get_metrics(nas_metrics_t* m) = 0;
virtual void stop() = 0;
void start_pcap(srslte::nas_pcap* pcap_);
void run_tti();
protected:
srslte::byte_buffer_pool* pool = nullptr;
rrc_interface_nas* rrc = nullptr;
usim_interface_nas* usim = nullptr;
gw_interface_nas* gw = nullptr;
// Task handler
srslte::task_sched_handle task_sched;
srslte::proc_manager_list_t callbacks;
// Logging reference
srslte::log_ref nas_log;
// PCAP
srslte::nas_pcap* pcap = nullptr;
// Security context
uint8_t k_nas_enc[32] = { };
uint8_t k_nas_int[32] = { };
struct nas_sec_ctxt {
uint8_t ksi;
uint8_t k_asme[32];
uint32_t tx_count;
uint32_t rx_count;
uint32_t k_enb_count;
srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo;
srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo;
LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti;
} ctxt = { };
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);
void cipher_encrypt(srslte::byte_buffer_t* pdu);
void cipher_decrypt(srslte::byte_buffer_t* pdu);
void set_k_enb_count(uint32_t count);
uint32_t get_k_enb_count();
};
class nas : public nas_base
{
public:
nas(srslte::task_sched_handle task_sched_, const nas_args_t& cfg_);
void init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_);
void stop();
void get_metrics(nas_metrics_t* m);
emm_state_t get_state();
@ -55,7 +108,6 @@ public:
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;
@ -72,17 +124,8 @@ public:
// timer callback
void timer_expired(uint32_t timeout_id) override;
// PCAP
void start_pcap(srslte::nas_pcap* pcap_);
private:
srslte::byte_buffer_pool* pool = nullptr;
srslte::log_ref nas_log;
rrc_interface_nas* rrc = nullptr;
usim_interface_nas* usim = nullptr;
gw_interface_nas* gw = nullptr;
nas_args_t cfg = {};
nas_args_t cfg = { };
emm_state_t state = EMM_STATE_DEREGISTERED;
@ -94,18 +137,6 @@ private:
std::vector<srslte::plmn_id_t> known_plmns;
// Security context
struct nas_sec_ctxt {
uint8_t ksi;
uint8_t k_asme[32];
uint32_t tx_count;
uint32_t rx_count;
uint32_t k_enb_count;
srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo;
srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo;
LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti;
};
typedef enum { DEFAULT_EPS_BEARER = 0, DEDICATED_EPS_BEARER } eps_bearer_type_t;
typedef struct {
@ -120,7 +151,6 @@ private:
bool have_guti = false;
bool have_ctxt = false;
nas_sec_ctxt ctxt = {};
bool auth_request = false;
uint8_t current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS;
@ -135,7 +165,6 @@ private:
uint8_t transaction_id = 0;
// timers
srslte::task_sched_handle task_sched;
srslte::timer_handler::unique_timer t3402; // started when attach attempt counter reached 5
srslte::timer_handler::unique_timer t3410; // started when attach request is sent, on expiry, start t3411
srslte::timer_handler::unique_timer t3411; // started when attach failed
@ -157,8 +186,6 @@ private:
// Security
bool eia_caps[8] = {};
bool eea_caps[8] = {};
uint8_t k_nas_enc[32] = {};
uint8_t k_nas_int[32] = {};
// Airplane mode simulation
typedef enum { DISABLED = 0, ENABLED } airplane_mode_state_t;
@ -169,16 +196,9 @@ private:
bool running = false;
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);
void cipher_encrypt(srslte::byte_buffer_t* pdu);
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);
void select_plmn();
@ -317,7 +337,6 @@ private:
nas* nas_ptr;
enum class state_t { plmn_search, rrc_connect } state = state_t::plmn_search;
};
srslte::proc_manager_list_t callbacks;
srslte::proc_t<plmn_search_proc> plmn_searcher;
srslte::proc_t<rrc_connect_proc> rrc_connector;

View File

@ -44,6 +44,15 @@ public:
nas_sim_args_t sim;
};
class nas_ext_args_t
{
public:
nas_ext_args_t() : enable(false) {}
bool enable;
std::string sock_path;
};
// EMM states (3GPP 24.302 v10.0.0)
typedef enum {
EMM_STATE_NULL = 0,

View File

@ -0,0 +1,103 @@
/*
* Copyright 2020 Software Radio Systems Limited
* Author: Vadim Yanitskiy <axilirator@gmail.com>
* Sponsored by Positive Technologies
*
* 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/.
*
*/
#ifndef SRSUE_NAS_EXT_H
#define SRSUE_NAS_EXT_H
#include "srslte/common/buffer_pool.h"
#include "srslte/common/common.h"
#include "srslte/common/log.h"
#include "srslte/common/nas_pcap.h"
#include "srslte/common/security.h"
#include "srslte/common/stack_procedure.h"
#include "srslte/interfaces/ue_interfaces.h"
#include "srsue/hdr/stack/upper/nas.h"
#include "srsue/hdr/stack/upper/nas_common.h"
#include "srsue/hdr/stack/upper/nas_ext.h"
#include "srsue/hdr/stack/upper/nas_extif.h"
#include "srsue/hdr/stack/upper/nas_metrics.h"
#include "srsue/hdr/stack/upper/rrctl.h"
using srslte::byte_buffer_t;
namespace srsue {
class nas_ext : public nas_base
{
public:
nas_ext(srslte::task_sched_handle task_sched_, const nas_ext_args_t& cfg_) :
nas_base::nas_base(task_sched_, "NAS"), cfg(cfg_) {};
void init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_);
void get_metrics(nas_metrics_t* m);
void stop();
// RRC interface
void left_rrc_connected();
bool paging(srslte::s_tmsi_t* ue_identity);
void set_barring(srslte::barring_t barring);
void write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu);
bool is_attached();
bool get_k_asme(uint8_t* k_asme_, uint32_t n);
uint32_t get_ipv4_addr();
bool get_ipv6_addr(uint8_t* ipv6_addr);
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;
// 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;
// timer callback
void timer_expired(uint32_t timeout_id);
private:
nas_ext_args_t cfg = {};
// Interface to an external NAS entity
std::unique_ptr<nas_extif_base> iface;
// RRCTL message handlers
void handle_rrctl_reset(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len);
void handle_rrctl_plmn_search(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len);
void handle_rrctl_plmn_select(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len);
void handle_rrctl_conn_establish(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len);
void handle_rrctl_conn_release(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len);
void handle_rrctl_data(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len);
void handle_rrctl_param(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len);
void handle_rrctl_sec_mode(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len);
void handle_rrctl_ext_usim(const uint8_t* msg, size_t len);
void handle_usim_gen_auth_resp_req(const struct rrctl::proto::ext_usim_msg* msg, size_t len);
void handle_usim_gen_nas_keys_req(const struct rrctl::proto::ext_usim_msg* msg, size_t len);
void rrctl_send_confirm(rrctl::proto::msg_type type);
void rrctl_send_error(rrctl::proto::msg_type type);
};
} // namespace srsue
#endif // SRSUE_NAS_EXT_H

View File

@ -0,0 +1,64 @@
/*
* Copyright 2020 Software Radio Systems Limited
* Author: Vadim Yanitskiy <axilirator@gmail.com>
* Sponsored by Positive Technologies
*
* 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/.
*
*/
#ifndef SRSUE_NAS_EXTIF_H
#define SRSUE_NAS_EXTIF_H
#include "srslte/common/common.h"
#include "srslte/common/log.h"
#include "srslte/common/logmap.h"
#include "srslte/common/threads.h"
using srslte::byte_buffer_t;
namespace srsue {
// Abstract class for an external interface
class nas_extif_base : public srslte::thread
{
public:
using recv_cb_t = std::function<void(const srslte::byte_buffer_t&)>;
nas_extif_base(recv_cb_t recv_cb_) :
recv_cb(std::move(recv_cb_)),
srslte::thread("EXTIF"),
if_log{"EXTIF"}
{ /* empty constructor */ };
// Interface for nas_ext
virtual void close(void) = 0;
virtual int write(const srslte::byte_buffer_t& pdu) = 0;
protected:
static const int IFACE_THREAD_PRIO = 65;
virtual void run_thread() = 0;
virtual void stop() = 0;
bool running = false;
recv_cb_t recv_cb;
srslte::log_ref if_log;
};
} // namespace srsue
#endif // SRSUE_NAS_EXTIF_H

View File

@ -0,0 +1,67 @@
/*
* Copyright 2020 Software Radio Systems Limited
* Author: Vadim Yanitskiy <axilirator@gmail.com>
* Sponsored by Positive Technologies
*
* 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/.
*
*/
#ifndef SRSUE_NAS_EXTIF_UNIX_H
#define SRSUE_NAS_EXTIF_UNIX_H
#include <boost/asio.hpp>
#include "srslte/common/block_queue.h"
#include "srslte/common/common.h"
#include "srslte/common/log.h"
#include "srsue/hdr/stack/upper/nas_extif.h"
namespace srsue {
// UNIX domain socket server
class nas_extif_unix : public nas_extif_base
{
public:
nas_extif_unix(recv_cb_t cb_, const std::string& sock_path_);
void close(void) override;
int write(const srslte::byte_buffer_t& pdu) override;
protected:
void run_thread(void) override;
void stop(void) override;
private:
std::unique_ptr<boost::asio::local::stream_protocol::acceptor> acc;
std::unique_ptr<boost::asio::local::stream_protocol::socket> sock;
boost::asio::io_context io_ctx;
std::string sock_path;
srslte::block_queue<srslte::byte_buffer_t> tx_queue;
bool has_connection;
uint8_t buf[1024];
void handle_write(void);
void handle_read(void);
void accept_conn(void);
};
} // namespace srsue
#endif // SRSUE_NAS_EXTIF_UNIX_H

View File

@ -0,0 +1,260 @@
/*
* Copyright 2020 Software Radio Systems Limited
* Author: Vadim Yanitskiy <axilirator@gmail.com>
* Sponsored by Positive Technologies
*
* 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 <stdint.h>
#include <asm/byteorder.h>
#include "srslte/common/common.h"
#include "srslte/interfaces/ue_interfaces.h"
namespace rrctl {
namespace proto {
enum msg_type {
RRCTL_RESET = 0x00,
RRCTL_DATA,
RRCTL_PLMN_SEARCH,
RRCTL_PLMN_SELECT,
RRCTL_CONN_ESTABLISH,
RRCTL_CONN_RELEASE,
RRCTL_PAGING,
RRCTL_PARAM,
RRCTL_SEC_MODE,
/* RRCTL protocol extensions (0b11xxxx, up to 15 groups) follow */
/* (U)SIM specific messages (for accessing built-in card reader) */
RRCTL_EXT_USIM = 0x3e, // 0b111110
/* RFU (Reserved for Further Use) */
RRCTL_RESERVED = 0x3f, // 0b111111
};
enum msg_disc {
RRCTL_REQ = 0x00,
RRCTL_IND = 0x01,
RRCTL_CNF = 0x02,
RRCTL_ERR = 0x03,
};
struct msg_hdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
uint8_t disc:2, type:6;
#elif defined(__BIG_ENDIAN_BITFIELD)
uint8_t type:6, disc:2;
#else
#error "Please fix <asm/byteorder.h>"
#endif
uint8_t rfu; // Reserved
uint16_t len;
uint8_t data[0];
} __attribute__((packed));
struct msg_plmn_search_res {
uint8_t nof_plmns;
struct plmn {
uint8_t mcc[2];
uint8_t mnc[2];
uint16_t tac;
} plmns[16];
} __attribute__((packed));
struct msg_plmn_select_req {
uint8_t mcc[2];
uint8_t mnc[2];
} __attribute__((packed));
struct msg_conn_establish_req {
uint8_t cause;
uint8_t pdu[0];
} __attribute__((packed));
struct msg_data {
uint32_t lcid;
uint8_t pdu[0];
} __attribute__((packed));
struct __mmec_m_tmsi {
uint8_t mmec;
uint32_t m_tmsi;
} __attribute__((packed));
struct msg_paging_ind {
struct __mmec_m_tmsi ueid;
} __attribute__((packed));
enum msg_param_type {
RRCTL_PARAM_UEID = 0x00,
};
struct msg_param_req {
uint8_t type;
uint8_t len;
union {
struct __mmec_m_tmsi ueid;
} u;
} __attribute__((packed));
enum msg_eea_type {
RRCTL_EEA0 = 0x00,
RRCTL_EEA1 = 0x01,
RRCTL_EEA2 = 0x02,
RRCTL_EEA3 = 0x03,
};
enum msg_eia_type {
RRCTL_EIA0 = 0x00,
RRCTL_EIA1 = 0x01,
RRCTL_EIA2 = 0x02,
RRCTL_EIA3 = 0x03,
};
#define SEC_MODE_F_RESET_RX_CTR (1 << 0)
#define SEC_MODE_F_RESET_TX_CTR (1 << 1)
struct msg_sec_mode_req {
#if defined(__LITTLE_ENDIAN_BITFIELD)
uint8_t flags:2, eia:3, eea:3;
#elif defined(__BIG_ENDIAN_BITFIELD)
uint8_t eea:3, eia:3, flags:2;
#else
#error "Please fix <asm/byteorder.h>"
#endif
uint8_t spare[3];
uint8_t k_asme[0]; // optional (32 octets)
} __attribute__((packed));
struct msg {
struct msg_hdr hdr;
union {
struct msg_data data;
struct msg_sec_mode_req;
struct msg_param_req param_req;
struct msg_paging_ind paging_ind;
struct msg_plmn_search_res plmn_search_res;
struct msg_plmn_select_req plmn_select_req;
struct msg_conn_establish_req conn_establish_req;
} u;
} __attribute__((packed));
/* (U)SIM specific message types */
enum ext_usim_msg_type {
EXT_USIM_RAW_APDU = 0x00,
EXT_USIM_READ_FILE = 0x01,
EXT_USIM_UPDATE_FILE = 0x02,
EXT_USIM_GEN_AUTH_RESP = 0x03,
/* RFU (Reserved for Further Use) */
EXT_USIM_RESERVED = 0xff,
};
struct ext_usim_raw_apdu {
uint16_t len;
uint8_t apdu_sw[0];
} __attribute__((packed));
struct ext_usim_read_file_req {
/* TODO: check GSM 11.11 and make sure that this is correct */
uint16_t df; // Dedicated File
uint16_t ef; // Elementary File
} __attribute__((packed));
struct ext_usim_read_file_rsp {
uint16_t len;
uint8_t data[0];
} __attribute__((packed));
struct ext_usim_update_file_req {
/* NOTE: re-using the existing structures here */
struct ext_usim_read_file_req file; // DF & EF
struct ext_usim_read_file_rsp content;
} __attribute__((packed));
struct ext_usim_gen_auth_resp_req {
uint8_t rand[16];
uint8_t autn[16];
uint8_t mcc[2];
uint8_t mnc[2];
} __attribute__((packed));
struct ext_usim_gen_auth_resp_rsp {
uint8_t out_of_sync;
uint8_t res_len;
uint8_t spare[2];
uint8_t k_asme_res[0];
} __attribute__((packed));
struct ext_usim_msg {
uint8_t type;
uint8_t spare[3];
union {
struct ext_usim_raw_apdu raw_apdu;
struct ext_usim_read_file_req read_file_req;
struct ext_usim_read_file_rsp read_file_rsp;
struct ext_usim_update_file_req update_file_req;
struct ext_usim_gen_auth_resp_req gen_auth_resp_req;
struct ext_usim_gen_auth_resp_rsp gen_auth_resp_rsp;
} u;
} __attribute__((packed));
std::string msg_hdr_desc(proto::msg_type type, proto::msg_disc disc, uint16_t len = 0);
} // namespace proto
namespace codec {
class error : public std::runtime_error {
public:
explicit error(const std::string& msg) : std::runtime_error(msg) {};
};
struct proto::msg_hdr* enc_hdr(srslte::byte_buffer_t& buf,
proto::msg_type type,
proto::msg_disc disc,
uint16_t len = 0);
const uint8_t* dec_hdr(const srslte::byte_buffer_t& buf,
proto::msg_type& type,
proto::msg_disc& disc,
uint16_t& len);
void enc_plmn_search_res(srslte::byte_buffer_t& buf,
const srsue::rrc_interface_nas::found_plmn_t* plmns,
size_t nof_plmns);
void dec_plmn_select_req(std::pair<uint16_t, uint16_t>& mcc_mnc,
const uint8_t* payload, size_t len);
void dec_conn_establish_req(srslte::establishment_cause_t& cause,
const uint8_t*& pdu, size_t& pdu_len,
const uint8_t* payload, size_t len);
void enc_data_ind(srslte::byte_buffer_t& buf,
const uint8_t *pdu, size_t pdu_len,
uint32_t lcid);
void enc_paging_ind(srslte::byte_buffer_t& buf,
srslte::s_tmsi_t* ue_identity);
} // namespace codec
} // namespace rrctl

View File

@ -139,6 +139,9 @@ static int parse_args(all_args_t* args, int argc, char* argv[])
("nas.eia", bpo::value<string>(&args->stack.nas.eia)->default_value("1,2,3"), "List of integrity algorithms included in UE capabilities")
("nas.eea", bpo::value<string>(&args->stack.nas.eea)->default_value("0,1,2,3"), "List of ciphering algorithms included in UE capabilities")
("extnas.enable", bpo::value<bool>(&args->stack.nas_ext.enable)->default_value(false), "Disable the built-in NAS implementation, provide external interface")
("extnas.sock_path", bpo::value<string>(&args->stack.nas_ext.sock_path)->default_value("/tmp/ue_extnas.sock"), "UNIX socket path of the external interface")
("pcap.enable", bpo::value<bool>(&args->stack.pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark")
("pcap.filename", bpo::value<string>(&args->stack.pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename")
("pcap.nas_enable", bpo::value<bool>(&args->stack.pcap.nas_enable)->default_value(false), "Enable NAS packet captures for wireshark")
@ -166,6 +169,8 @@ static int parse_args(all_args_t* args, int argc, char* argv[])
("log.usim_hex_limit", bpo::value<int>(&args->stack.log.usim_hex_limit), "USIM log hex dump limit")
("log.stack_level", bpo::value<string>(&args->stack.log.stack_level), "Stack log level")
("log.stack_hex_limit", bpo::value<int>(&args->stack.log.stack_hex_limit), "Stack log hex dump limit")
("log.extif_level", bpo::value<string>(&args->stack.log.extif_level), "External interface log level")
("log.extif_hex_limit", bpo::value<int>(&args->stack.log.extif_hex_limit), "External interface log hex dump limit")
("log.all_level", bpo::value<string>(&args->log.all_level)->default_value("info"), "ALL log level")
("log.all_hex_limit", bpo::value<int>(&args->log.all_hex_limit)->default_value(32), "ALL log hex dump limit")
@ -535,6 +540,9 @@ static int parse_args(all_args_t* args, int argc, char* argv[])
if (!vm.count("log.stack_level")) {
args->stack.log.stack_level = args->log.all_level;
}
if (!vm.count("log.extif_level")) {
args->stack.log.extif_level = args->log.all_level;
}
}
// Apply all_hex_limit to any unset layers
@ -566,6 +574,9 @@ static int parse_args(all_args_t* args, int argc, char* argv[])
if (!vm.count("log.stack_hex_limit")) {
args->stack.log.stack_hex_limit = args->log.all_hex_limit;
}
if (!vm.count("log.extif_hex_limit")) {
args->stack.log.extif_hex_limit = args->log.all_hex_limit;
}
}
// Set sync queue capacity to 1 for ZMQ

View File

@ -37,11 +37,11 @@ ue_stack_lte::ue_stack_lte() :
logger(nullptr),
usim(nullptr),
phy(nullptr),
nas(nullptr),
rlc("RLC"),
mac("MAC", &task_sched),
rrc(this, &task_sched),
pdcp(&task_sched, "PDCP"),
nas(&task_sched),
thread("STACK"),
task_sched(512, 2, 64),
tti_tprof("tti_tprof", "STCK", TTI_STAT_PERIOD)
@ -102,6 +102,17 @@ int ue_stack_lte::init(const stack_args_t& args_, srslte::logger* logger_)
usim_log->set_hex_limit(args.log.usim_hex_limit);
nas_log->set_level(args.log.nas_level);
nas_log->set_hex_limit(args.log.nas_hex_limit);
extif_log->set_level(args.log.extif_level);
extif_log->set_hex_limit(args.log.extif_hex_limit);
// Should we use the built-in NAS implementation
if (!args.nas_ext.enable) {
std::unique_ptr<srsue::nas> nas_impl(new srsue::nas(&task_sched, args.nas));
nas = std::move(nas_impl);
} else { // ... or provide an external interface (RRCTL)?
std::unique_ptr<srsue::nas_ext> nas_impl(new srsue::nas_ext(&task_sched, args.nas_ext));
nas = std::move(nas_impl);
}
// Set up pcap
if (args.pcap.enable) {
@ -110,7 +121,7 @@ int ue_stack_lte::init(const stack_args_t& args_, srslte::logger* logger_)
}
if (args.pcap.nas_enable) {
nas_pcap.open(args.pcap.nas_filename.c_str());
nas.start_pcap(&nas_pcap);
nas->start_pcap(&nas_pcap);
}
// Init USIM first to allow early exit in case reader couldn't be found
@ -126,8 +137,8 @@ int ue_stack_lte::init(const stack_args_t& args_, srslte::logger* logger_)
mac.init(phy, &rlc, &rrc);
rlc.init(&pdcp, &rrc, task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */);
pdcp.init(&rlc, &rrc, gw);
nas.init(usim.get(), &rrc, gw, args.nas);
rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, args.rrc);
nas->init(usim.get(), &rrc, gw);
rrc.init(phy, &mac, &rlc, &pdcp, nas.get(), usim.get(), gw, args.rrc);
running = true;
start(STACK_MAIN_THREAD_PRIO);
@ -148,7 +159,7 @@ void ue_stack_lte::stop_impl()
running = false;
usim->stop();
nas.stop();
nas->stop();
rrc.stop();
rlc.stop();
@ -166,7 +177,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); });
ue_task_queue.try_push([this]() { nas->start_attach_proc(nullptr, srslte::establishment_cause_t::mo_sig); });
return true;
}
return false;
@ -175,7 +186,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->detach_request(true);
// 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;
@ -202,7 +213,7 @@ bool ue_stack_lte::disable_data()
{
// generate detach request
srslte::console("Turning on airplane mode.\n");
return nas.detach_request(false);
return nas->detach_request(false);
}
bool ue_stack_lte::get_metrics(stack_metrics_t* metrics)
@ -212,7 +223,7 @@ bool ue_stack_lte::get_metrics(stack_metrics_t* metrics)
stack_metrics_t metrics{};
mac.get_metrics(metrics.mac);
rlc.get_metrics(metrics.rlc);
nas.get_metrics(&metrics.nas);
nas->get_metrics(&metrics.nas);
rrc.get_metrics(metrics.rrc);
pending_stack_metrics.push(metrics);
});
@ -313,7 +324,7 @@ void ue_stack_lte::run_tti_impl(uint32_t tti, uint32_t tti_jump)
task_sched.tic();
}
rrc.run_tti();
nas.run_tti();
nas->run_tti();
if (args.have_tti_time_stats) {
std::chrono::nanoseconds dur = tti_tprof.stop();

View File

@ -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 gw.cc nas.cc nas_ext.cc nas_extif.cc rrctl.cc usim_base.cc usim.cc tft_packet_filter.cc)
if(HAVE_PCSC)
list(APPEND SOURCES "pcsc_usim.cc")

View File

@ -235,20 +235,25 @@ proc_outcome_t nas::rrc_connect_proc::react(nas::rrc_connect_proc::connection_re
* NAS
********************************************************************/
nas::nas(srslte::task_sched_handle task_sched_) :
nas_base::nas_base(srslte::task_sched_handle task_sched_, const char *log_name_) :
pool(byte_buffer_pool::get_instance()),
task_sched(task_sched_),
nas_log{log_name_}
{}
nas::nas(srslte::task_sched_handle task_sched_, const nas_args_t& cfg_) :
nas_base::nas_base(task_sched_, "NAS"),
cfg(cfg_),
plmn_searcher(this),
rrc_connector(this),
task_sched(task_sched_),
t3402(task_sched_.get_unique_timer()),
t3410(task_sched_.get_unique_timer()),
t3411(task_sched_.get_unique_timer()),
t3421(task_sched_.get_unique_timer()),
reattach_timer(task_sched_.get_unique_timer()),
nas_log{"NAS"}
reattach_timer(task_sched_.get_unique_timer())
{}
void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& cfg_)
void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_)
{
usim = usim_;
rrc = rrc_;
@ -262,7 +267,7 @@ void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_
// parse and sanity check EIA list
std::vector<uint8_t> cap_list;
srslte::string_parse_list(cfg_.eia, ',', cap_list);
srslte::string_parse_list(cfg.eia, ',', cap_list);
if (cap_list.empty()) {
nas_log->error("Empty EIA list. Select at least one EIA algorithm.\n");
}
@ -275,7 +280,7 @@ void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_
}
// parse and sanity check EEA list
srslte::string_parse_list(cfg_.eea, ',', cap_list);
srslte::string_parse_list(cfg.eea, ',', cap_list);
if (cap_list.empty()) {
nas_log->error("Empty EEA list. Select at least one EEA algorithm.\n");
}
@ -287,8 +292,6 @@ void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_
}
}
cfg = cfg_;
if ((read_ctxt_file(&ctxt))) {
usim->generate_nas_keys(ctxt.k_asme, k_nas_enc, k_nas_int, ctxt.cipher_algo, ctxt.integ_algo);
nas_log->debug_hex(k_nas_enc, 32, "NAS encryption key - k_nas_enc");
@ -326,7 +329,7 @@ emm_state_t nas::get_state()
return state;
}
void nas::run_tti()
void nas_base::run_tti()
{
callbacks.run();
}
@ -710,14 +713,14 @@ void nas::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu)
}
}
void nas::set_k_enb_count(uint32_t count)
void nas_base::set_k_enb_count(uint32_t count)
{
// UL count for RRC key derivation depends on UL Count of the Attach Request or Service Request.
// On the case of an Authentication Request, the UL count used to generate K_enb must be reset to zero.
ctxt.k_enb_count = count;
}
uint32_t nas::get_k_enb_count()
uint32_t nas_base::get_k_enb_count()
{
return ctxt.k_enb_count;
}
@ -756,7 +759,7 @@ bool nas::get_ipv6_addr(uint8_t* ipv6_addr)
PCAP
*******************************************************************************/
void nas::start_pcap(srslte::nas_pcap* pcap_)
void nas_base::start_pcap(srslte::nas_pcap* pcap_)
{
pcap = pcap_;
}
@ -765,12 +768,12 @@ void nas::start_pcap(srslte::nas_pcap* pcap_)
* Security
******************************************************************************/
void nas::integrity_generate(uint8_t* key_128,
uint32_t count,
uint8_t direction,
uint8_t* msg,
uint32_t msg_len,
uint8_t* mac)
void nas_base::integrity_generate(uint8_t* key_128,
uint32_t count,
uint8_t direction,
uint8_t* msg,
uint32_t msg_len,
uint8_t* mac)
{
switch (ctxt.integ_algo) {
case INTEGRITY_ALGORITHM_ID_EIA0:
@ -809,7 +812,7 @@ void nas::integrity_generate(uint8_t* key_128,
// This function depends to a valid k_nas_int.
// This key is generated in the security mode command.
bool nas::integrity_check(byte_buffer_t* pdu)
bool nas_base::integrity_check(byte_buffer_t* pdu)
{
if (pdu == nullptr) {
nas_log->error("Invalid PDU\n");
@ -863,7 +866,7 @@ bool nas::integrity_check(byte_buffer_t* pdu)
}
}
void nas::cipher_encrypt(byte_buffer_t* pdu)
void nas_base::cipher_encrypt(byte_buffer_t* pdu)
{
byte_buffer_t pdu_tmp;
switch (ctxt.cipher_algo) {
@ -905,7 +908,7 @@ void nas::cipher_encrypt(byte_buffer_t* pdu)
}
}
void nas::cipher_decrypt(byte_buffer_t* pdu)
void nas_base::cipher_decrypt(byte_buffer_t* pdu)
{
byte_buffer_t tmp_pdu;
switch (ctxt.cipher_algo) {

View File

@ -0,0 +1,535 @@
/*
* Copyright 2020 Software Radio Systems Limited
* Author: Vadim Yanitskiy <axilirator@gmail.com>
* Sponsored by Positive Technologies
*
* 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 "srslte/common/buffer_pool.h"
#include "srslte/common/common.h"
#include "srslte/common/log.h"
#include "srslte/common/nas_pcap.h"
#include "srslte/common/security.h"
#include "srslte/common/stack_procedure.h"
#include "srslte/interfaces/ue_interfaces.h"
#include "srsue/hdr/stack/upper/nas.h"
#include "srsue/hdr/stack/upper/nas_common.h"
#include "srsue/hdr/stack/upper/nas_ext.h"
#include "srsue/hdr/stack/upper/nas_extif_unix.h"
#include "srsue/hdr/stack/upper/nas_metrics.h"
using namespace srslte;
namespace srsue {
void nas_ext::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_)
{
usim = usim_;
rrc = rrc_;
gw = gw_;
// RRCTL PDU handler
auto rrctl_rx_cb = [this](const srslte::byte_buffer_t& pdu) {
rrctl::proto::msg_type type;
rrctl::proto::msg_disc disc;
const uint8_t* payload;
std::string desc;
uint16_t length;
// Parse the message header
try {
payload = rrctl::codec::dec_hdr(pdu, type, disc, length);
desc = rrctl::proto::msg_hdr_desc(type, disc, length);
nas_log->info("Got RRCTL message: %s\n", desc.c_str());
} catch (const rrctl::codec::error& e) {
nas_log->warning("Got malformed RRCTL message: %s\n", e.what());
return;
}
// Call the corresponding handler
switch (type) {
case rrctl::proto::RRCTL_RESET:
handle_rrctl_reset(disc, payload, length);
break;
case rrctl::proto::RRCTL_PLMN_SEARCH:
handle_rrctl_plmn_search(disc, payload, length);
break;
case rrctl::proto::RRCTL_PLMN_SELECT:
handle_rrctl_plmn_select(disc, payload, length);
break;
case rrctl::proto::RRCTL_CONN_ESTABLISH:
handle_rrctl_conn_establish(disc, payload, length);
break;
case rrctl::proto::RRCTL_DATA:
handle_rrctl_data(disc, payload, length);
break;
case rrctl::proto::RRCTL_PARAM:
handle_rrctl_param(disc, payload, length);
break;
case rrctl::proto::RRCTL_SEC_MODE:
handle_rrctl_sec_mode(disc, payload, length);
break;
case rrctl::proto::RRCTL_EXT_USIM:
handle_rrctl_ext_usim(payload, length);
break;
case rrctl::proto::RRCTL_CONN_RELEASE:
default:
nas_log->warning("%s is not handled\n", desc.c_str());
}
};
std::unique_ptr<nas_extif_unix> iface_(new nas_extif_unix(rrctl_rx_cb, cfg.sock_path));
iface = std::move(iface_);
}
void nas_ext::rrctl_send_confirm(rrctl::proto::msg_type type)
{
srslte::byte_buffer_t pdu;
rrctl::codec::enc_hdr(pdu, type, rrctl::proto::RRCTL_CNF);
iface->write(pdu);
}
void nas_ext::rrctl_send_error(rrctl::proto::msg_type type)
{
srslte::byte_buffer_t pdu;
rrctl::codec::enc_hdr(pdu, type, rrctl::proto::RRCTL_ERR);
iface->write(pdu);
}
void nas_ext::handle_rrctl_reset(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len)
{
/* Reset security state (no EEA, no EIA) */
memset(&ctxt, 0x00, sizeof(ctxt));
rrctl_send_confirm(rrctl::proto::RRCTL_RESET);
}
void nas_ext::handle_rrctl_plmn_search(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len)
{
// Response to be sent when RRC responds
rrc->plmn_search();
}
void nas_ext::handle_rrctl_plmn_select(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len)
{
std::pair<uint16_t, uint16_t> mcc_mnc;
srslte::plmn_id_t plmn_id;
// Parse PLMN ID
try {
rrctl::codec::dec_plmn_select_req(mcc_mnc, msg, len);
} catch (const rrctl::codec::error& e) {
nas_log->warning("Failed to parse RRCTL message: %s\n", e.what());
rrctl_send_error(rrctl::proto::RRCTL_PLMN_SELECT);
return;
}
if (plmn_id.from_number(mcc_mnc.first, mcc_mnc.second) != SRSLTE_SUCCESS) {
nas_log->warning("Failed to parse PLMN ID from PLMN Select Request\n");
rrctl_send_error(rrctl::proto::RRCTL_PLMN_SELECT);
}
rrc->plmn_select(plmn_id);
rrctl_send_confirm(rrctl::proto::RRCTL_PLMN_SELECT);
}
void nas_ext::handle_rrctl_conn_establish(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len)
{
srslte::establishment_cause_t cause;
const uint8_t* pdu;
size_t pdu_len;
try {
rrctl::codec::dec_conn_establish_req(cause, pdu, pdu_len, msg, len);
} catch (const rrctl::codec::error& e) {
nas_log->warning("Failed to parse RRCTL message: %s\n", e.what());
rrctl_send_error(rrctl::proto::RRCTL_CONN_ESTABLISH);
return;
}
set_k_enb_count(ctxt.tx_count);
ctxt.tx_count++;
// Allocate a new NAS PDU on heap
unique_byte_buffer_t nas_pdu = srslte::allocate_unique_buffer(*pool, true);
nas_pdu->append_bytes(pdu, pdu_len);
if (pcap != nullptr)
pcap->write_nas(nas_pdu->msg, nas_pdu->N_bytes);
rrc->connection_request(cause, std::move(nas_pdu));
}
void nas_ext::handle_rrctl_data(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len)
{
if (not rrc->is_connected()) {
nas_log->warning("Received DATA.req, but there is no active connection\n");
rrctl_send_error(rrctl::proto::RRCTL_DATA);
return;
}
// Allocate a new NAS PDU on heap
unique_byte_buffer_t nas_pdu = srslte::allocate_unique_buffer(*pool, true);
nas_pdu->append_bytes(msg, len);
if (pcap != nullptr)
pcap->write_nas(nas_pdu->msg, nas_pdu->N_bytes);
// Apply pre-configured EEA algorythm (if enabled)
cipher_encrypt(nas_pdu.get());
// Apply pre-configured EIA algorythm (if enabled)
integrity_generate(&k_nas_int[16], ctxt.tx_count, SECURITY_DIRECTION_UPLINK,
&nas_pdu->msg[5], nas_pdu->N_bytes - 5, &nas_pdu->msg[1]);
rrc->write_sdu(std::move(nas_pdu));
ctxt.tx_count++;
rrctl_send_confirm(rrctl::proto::RRCTL_DATA);
}
void nas_ext::handle_rrctl_param(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len)
{
const struct rrctl::proto::msg_param_req* param;
srslte::s_tmsi_t ue_identity;
param = reinterpret_cast<const struct rrctl::proto::msg_param_req*> (msg);
nas_log->warning("Rx PARAM.req (type=%02x, len=%u)\n", param->type, param->len);
if (param->len != (len - 2)) { /* XXX: type + length */
nas_log->info("Received malformed PARAM.req (len=%u)\n", param->len);
rrctl_send_error(rrctl::proto::RRCTL_PARAM);
return;
}
switch (param->type) {
case rrctl::proto::RRCTL_PARAM_UEID:
ue_identity.m_tmsi = ntohl(param->u.ueid.m_tmsi);
ue_identity.mmec = param->u.ueid.mmec;
nas_log->info("Setting UEID: mmec=%x m_tmsi=%x\n",
ue_identity.mmec, ue_identity.m_tmsi);
rrc->set_ue_identity(ue_identity);
rrctl_send_confirm(rrctl::proto::RRCTL_PARAM);
break;
default:
nas_log->warning("Unhandled PARAM.req type (0x%02x)\n", param->type);
rrctl_send_error(rrctl::proto::RRCTL_PARAM);
}
}
void nas_ext::handle_rrctl_sec_mode(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len)
{
const struct rrctl::proto::msg_sec_mode_req* req;
req = reinterpret_cast<const struct rrctl::proto::msg_sec_mode_req*> (msg);
if (len < sizeof(*req)) { /* XXX: mandatory fields only */
nas_log->error("Received malformed PARAM.req (len=%zu)\n", len);
rrctl_send_error(rrctl::proto::RRCTL_SEC_MODE);
return;
}
/* Skip the header */
len -= sizeof(*req);
nas_log->info("Rx SecurityMode.req (EEA%u, EIA%u)\n", req->eea, req->eia);
ctxt.cipher_algo = static_cast<srslte::CIPHERING_ALGORITHM_ID_ENUM> (req->eea);
ctxt.integ_algo = static_cast<srslte::INTEGRITY_ALGORITHM_ID_ENUM> (req->eia);
if (req->flags & SEC_MODE_F_RESET_RX_CTR)
ctxt.rx_count = 0;
if (req->flags & SEC_MODE_F_RESET_TX_CTR)
ctxt.tx_count = 0;
if (req->eea != 0x00 or req->eia != 0x00) {
/* Ensure that Kasme is present */
if (len != sizeof(ctxt.k_asme)) {
nas_log->error("Kasme is expected, but not present");
rrctl_send_error(rrctl::proto::RRCTL_SEC_MODE);
return;
}
memcpy(&ctxt.k_asme[0], &req->k_asme[0], sizeof(ctxt.k_asme));
/* Derive both Knas_enc and Knas_int from Kasme */
/* NOTE: how is this related to (U)SIM at all?!? */
usim->generate_nas_keys(&ctxt.k_asme[0], // in
&k_nas_enc[0], // out
&k_nas_int[0], // out
ctxt.cipher_algo,
ctxt.integ_algo);
}
rrctl_send_confirm(rrctl::proto::RRCTL_SEC_MODE);
}
void nas_ext::handle_rrctl_ext_usim(const uint8_t* _msg, size_t len)
{
enum rrctl::proto::ext_usim_msg_type msg_type;
const struct rrctl::proto::ext_usim_msg* msg;
std::string msg_desc;
if (len < 4) { /* XXX: type + padding */
nas_log->error("Received too short (U)SIM specific message (len=%zu)\n", len);
rrctl_send_error(rrctl::proto::RRCTL_EXT_USIM);
return;
}
/* Skip the header */
len -= 4;
msg = reinterpret_cast<const struct rrctl::proto::ext_usim_msg*> (_msg);
msg_type = static_cast<rrctl::proto::ext_usim_msg_type> (msg->type);
switch (msg_type) {
case rrctl::proto::EXT_USIM_GEN_AUTH_RESP:
handle_usim_gen_auth_resp_req(msg, len);
break;
case rrctl::proto::EXT_USIM_RAW_APDU:
case rrctl::proto::EXT_USIM_READ_FILE:
case rrctl::proto::EXT_USIM_UPDATE_FILE:
case rrctl::proto::EXT_USIM_RESERVED:
default:
nas_log->warning("(U)SIM specific message 0x%02x is not handled\n", msg->type);
}
}
void nas_ext::handle_usim_gen_auth_resp_req(const struct rrctl::proto::ext_usim_msg* msg, size_t len)
{
const struct rrctl::proto::ext_usim_gen_auth_resp_req* req;
struct rrctl::proto::ext_usim_gen_auth_resp_rsp* rsp;
struct rrctl::proto::ext_usim_msg* rsp_msg;
std::pair<uint16_t, uint16_t> mcc_mnc;
struct rrctl::proto::msg_hdr* hdr;
srslte::byte_buffer_t pdu;
/* Allocate the response message in advance */
hdr = rrctl::codec::enc_hdr(pdu, rrctl::proto::RRCTL_EXT_USIM, rrctl::proto::RRCTL_CNF, 4);
rsp_msg = reinterpret_cast<struct rrctl::proto::ext_usim_msg*> (&pdu.msg[pdu.N_bytes]);
rsp_msg->type = msg->type;
pdu.N_bytes += 4;
if (len < sizeof(*req)) {
nas_log->error("Received too short (U)SIM GenAuthResp.req (len=%zu)\n", len);
hdr->disc = (uint8_t) rrctl::proto::RRCTL_ERR;
iface->write(pdu);
return;
}
rsp = &rsp_msg->u.gen_auth_resp_rsp;
req = &msg->u.gen_auth_resp_req;
// Parse PLMN ID
try {
/* HACK: generalize this function */
rrctl::codec::dec_plmn_select_req(mcc_mnc, &req->mcc[0], len);
} catch (const rrctl::codec::error& e) {
nas_log->warning("Failed to parse MCC/MNC: %s\n", e.what());
hdr->disc = (uint8_t) rrctl::proto::RRCTL_ERR;
iface->write(pdu);
return;
}
uint8_t k_asme[32];
uint8_t res[16];
int res_len;
/* NOTE: this is a blocking call => no other RRCTL messages can be processed in parallel?
* FIXME: the authors of srsUE apparently are not aware of 'const', so we have to cast. */
auth_result_t auth_res = usim->generate_authentication_response((uint8_t*) &req->rand[0],
(uint8_t*) &req->autn[0],
mcc_mnc.first, mcc_mnc.second,
&res[0], &res_len, &k_asme[0]);
switch (auth_res) {
case AUTH_OK:
nas_log->info("Authentication vector has been generated successfully\n");
set_k_enb_count(0);
pdu.N_bytes += sizeof(*rsp);
pdu.append_bytes(&k_asme[0], sizeof(k_asme));
pdu.append_bytes(&res[0], res_len);
rsp->res_len = (uint8_t) res_len;
break;
case AUTH_SYNCH_FAILURE:
nas_log->warning("Synchronization is required to generate an authentication vector\n");
pdu.N_bytes += sizeof(*rsp);
pdu.append_bytes(&res[0], res_len);
rsp->res_len = (uint8_t) res_len;
rsp->out_of_sync = true;
break;
case AUTH_FAILED:
default:
nas_log->warning("Could not generate an authentication vector\n");
hdr->disc = (uint8_t) rrctl::proto::RRCTL_ERR;
}
hdr->len = htons(pdu.N_bytes - sizeof(*hdr));
iface->write(pdu);
}
void nas_ext::get_metrics(nas_metrics_t* m)
{
nas_metrics_t metrics = {};
// FIXME: is there anything we could fill in?
*m = metrics;
}
void nas_ext::stop()
{
// Close the UNIX domain socket connection
iface->close();
iface.release();
}
/*******************************************************************************
* UE interface (dummy)
******************************************************************************/
void nas_ext::start_attach_proc(srslte::proc_state_t* result, srslte::establishment_cause_t cause_)
{
nas_log->info("The UE has requested us to perform Attach Request, however we ignore it\n");
if (result != nullptr) {
result->set_val();
}
}
bool nas_ext::detach_request(const bool switch_off)
{
nas_log->info("The UE has requested us to perform Detach Request, however we ignore it\n");
return false;
}
void nas_ext::timer_expired(uint32_t timeout_id)
{
nas_log->info("Timer id=%u is expired, however we ignore it\n", timeout_id);
}
/*******************************************************************************
* RRC interface
******************************************************************************/
// TODO: investigate the meaning of these signals
void nas_ext::set_barring(barring_t barring) {}
void nas_ext::left_rrc_connected() {}
bool nas_ext::paging(srslte::s_tmsi_t* ue_identity)
{
srslte::byte_buffer_t msg;
nas_log->info("Received paging from RRC\n");
rrctl::codec::enc_paging_ind(msg, ue_identity);
iface->write(msg);
// TODO: what are we supposed to return?
return false;
}
void nas_ext::write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu)
{
srslte::byte_buffer_t msg;
uint8 pd, sec_hdr_type;
nas_log->info_hex(pdu->msg, pdu->N_bytes, "Received DL %s PDU from RRC\n", rrc->get_rb_name(lcid).c_str());
// Parse the message security header
liblte_mme_parse_msg_sec_header((LIBLTE_BYTE_MSG_STRUCT*) pdu.get(), &pd, &sec_hdr_type);
switch (sec_hdr_type) {
case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT:
case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT:
case LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST:
case LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS:
case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY:
break;
case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED:
// Apply pre-configured EEA algorythm (if enabled)
cipher_decrypt(pdu.get());
break;
default:
nas_log->error("Received DL NAS PDU with unknown sec_hdr=%02x\n", sec_hdr_type);
}
if (pcap != nullptr)
pcap->write_nas(pdu->msg, pdu->N_bytes);
rrctl::codec::enc_data_ind(msg, pdu->msg, pdu->N_bytes, lcid);
iface->write(msg);
}
bool nas_ext::is_attached()
{
// FIXME: we probably need to maintain the state
return false; // return a dummy value for now
}
bool nas_ext::get_k_asme(uint8_t* k_asme_, uint32_t n)
{
// FIXME: we probably need to maintain a security context
return false; // return a dummy value for now
}
uint32_t nas_ext::get_ipv4_addr()
{
// FIXME: where can we get it? maybe from GW?
return 0x00000000;
}
bool nas_ext::get_ipv6_addr(uint8_t* ipv6_addr)
{
// FIXME: where can we get it? maybe from GW?
return false;
}
void nas_ext::plmn_search_completed(
const rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS],
int nof_plmns)
{
srslte::byte_buffer_t pdu;
nas_log->info("RRC has completed PLMN search, %d carriers found\n", nof_plmns);
// Send PLMN_SEARCH.res to an external entity
if (nof_plmns >= 0) {
rrctl::codec::enc_plmn_search_res(pdu, found_plmns, nof_plmns);
iface->write(pdu);
} else {
nas_log->warning("PLMN search completed with an error\n");
rrctl_send_error(rrctl::proto::RRCTL_PLMN_SEARCH);
}
}
bool nas_ext::connection_request_completed(bool outcome)
{
nas_log->info("RRC has %s connection establisment\n", outcome ? "completed" : "failed");
if (outcome)
rrctl_send_confirm(rrctl::proto::RRCTL_CONN_ESTABLISH);
else
rrctl_send_error(rrctl::proto::RRCTL_CONN_ESTABLISH);
return false; // FIXME: what should we return here?
}
} // namespace srsue

View File

@ -0,0 +1,158 @@
/*
* Copyright 2020 Software Radio Systems Limited
* Author: Vadim Yanitskiy <axilirator@gmail.com>
* Sponsored by Positive Technologies
*
* 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 "srslte/common/common.h"
#include "srslte/common/log.h"
#include "srsue/hdr/stack/upper/nas_extif_unix.h"
using namespace boost::asio::local;
namespace srsue {
nas_extif_unix::nas_extif_unix(recv_cb_t cb_, const std::string& sock_path_) :
nas_extif_base(cb_), sock_path(sock_path_), has_connection(false)
{
if_log->info("Init external NAS interface at '%s'\n", sock_path.c_str());
// Remove previous binding if present
unlink(sock_path.c_str());
// Set up a UNIX domain socket
stream_protocol::endpoint ep(sock_path);
stream_protocol::socket* sock_ = new stream_protocol::socket(io_ctx);
stream_protocol::acceptor* acc_ = new stream_protocol::acceptor(io_ctx, ep);
// Move ownership to this->acc
std::unique_ptr<stream_protocol::acceptor> acc_ptr(acc_);
acc = std::move(acc_ptr);
// Move ownership to this->sock
std::unique_ptr<stream_protocol::socket> sock_ptr(sock_);
sock = std::move(sock_ptr);
// Welcome the first connection
accept_conn();
if_log->info("Starting the server...\n");
start(IFACE_THREAD_PRIO);
};
void nas_extif_unix::handle_write(void)
{
srslte::byte_buffer_t pdu;
if (not tx_queue.try_pop(&pdu))
return;
boost::asio::async_write(*sock, boost::asio::buffer(pdu.msg, pdu.N_bytes),
[this](boost::system::error_code ec, std::size_t len)
{
if (!ec) {
if_log->info("Tx %zu bytes to external NAS interface\n", len);
} else {
if_log->warning("External NAS write() handler got error (ec=%d)\n", ec.value());
}
// Keep writing unless the queue is empty
handle_write();
});
}
void nas_extif_unix::handle_read(void)
{
sock->async_read_some(boost::asio::buffer(buf),
[this](boost::system::error_code ec, std::size_t len)
{
if (!ec) {
if_log->info("Rx %zu bytes from external NAS interface\n", len);
// Invoke the Rx callback
srslte::byte_buffer_t pdu;
pdu.append_bytes(buf, len);
recv_cb(pdu);
// Keep reading
handle_read();
} else {
if_log->info("External NAS interface has lost connection\n");
has_connection = false;
sock->release();
}
});
}
void nas_extif_unix::accept_conn(void)
{
acc->async_accept(
[this](boost::system::error_code ec, stream_protocol::socket sock_)
{
if (!ec) {
if (!has_connection) {
if_log->info("Accepted connection on external NAS interface\n");
*sock = std::move(sock_);
has_connection = true;
handle_read();
} else {
if_log->warning("NAS interface already has an active connection, rejecting...\n");
boost::asio::write(sock_, boost::asio::buffer("REJECT"));
}
} else {
if_log->warning("External NAS connection handler got error (ec=%d)\n", ec.value());
}
// Keep waiting for a new connection
accept_conn();
});
}
void nas_extif_unix::run_thread(void)
{
// This is a blocking call
io_ctx.run();
}
void nas_extif_unix::stop(void)
{
io_ctx.stop();
wait_thread_finish();
}
void nas_extif_unix::close(void)
{
stop();
}
int nas_extif_unix::write(const srslte::byte_buffer_t& pdu)
{
if (!has_connection) {
if_log->error("External NAS entity is not connected\n");
return -EIO;
}
tx_queue.push(pdu);
handle_write();
return 0;
}
} // namespace srsue

View File

@ -0,0 +1,229 @@
/*
* Copyright 2020 Software Radio Systems Limited
* Author: Vadim Yanitskiy <axilirator@gmail.com>
* Sponsored by Positive Technologies
*
* 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 <stdint.h>
#include <arpa/inet.h>
#include "srsue/hdr/stack/upper/rrctl.h"
namespace rrctl {
namespace proto {
std::string msg_hdr_desc(proto::msg_type type, proto::msg_disc disc, uint16_t len)
{
std::string desc;
switch (type) {
case RRCTL_RESET:
desc += "Reset";
break;
case RRCTL_PLMN_SEARCH:
desc += "PLMN Search";
break;
case RRCTL_PLMN_SELECT:
desc += "PLMN Select";
break;
case RRCTL_CONN_ESTABLISH:
desc += "Connection Establish";
break;
case RRCTL_CONN_RELEASE:
desc += "Connection Release";
break;
case RRCTL_DATA:
desc += "Data (PDU)";
break;
case RRCTL_PAGING:
desc += "Paging";
break;
case RRCTL_PARAM:
desc += "Parameter";
break;
case RRCTL_SEC_MODE:
desc += "Security Mode";
break;
case RRCTL_EXT_USIM:
desc += "(U)SIM Specific";
break;
case RRCTL_RESERVED:
desc += "RFU (Reserved for Further Use)";
break;
default:
desc += "<UNKNOWN>";
}
desc += " ";
switch (disc) {
case RRCTL_REQ:
desc += "Request";
break;
case RRCTL_IND:
desc += "Indication";
break;
case RRCTL_CNF:
desc += "Confirmation";
break;
case RRCTL_ERR:
desc += "Error";
break;
}
if (len > 0) {
desc += " (length ";
desc += std::to_string(len);
desc += ")";
}
return desc;
}
}
namespace codec {
struct proto::msg_hdr* enc_hdr(srslte::byte_buffer_t& buf,
proto::msg_type type,
proto::msg_disc disc,
uint16_t len)
{
struct proto::msg_hdr hdr = {
#if defined(__LITTLE_ENDIAN_BITFIELD)
.disc = (uint8_t) disc,
.type = (uint8_t) type,
#elif defined(__BIG_ENDIAN_BITFIELD)
.type = (uint8_t) type,
.disc = (uint8_t) disc,
#else
#error "Please fix <asm/byteorder.h>"
#endif
.len = ntohs(len),
};
buf.append_bytes((const uint8_t*) &hdr, sizeof(hdr));
return reinterpret_cast<struct proto::msg_hdr*> (&buf.msg[0]);
}
const uint8_t* dec_hdr(const srslte::byte_buffer_t& buf,
proto::msg_type& type,
proto::msg_disc& disc,
uint16_t& len)
{
const struct proto::msg_hdr* hdr;
// Make sure at least header is present
if (buf.N_bytes < sizeof(*hdr))
throw codec::error("header is too short");
hdr = reinterpret_cast<const struct proto::msg_hdr*> (buf.msg);
type = static_cast<proto::msg_type> (hdr->type);
disc = static_cast<proto::msg_disc> (hdr->disc);
len = htons(hdr->len);
// Make sure the whole message fits
if (buf.N_bytes < sizeof(*hdr) + len)
throw codec::error("body is too short");
// Return pointer to the payload (if present)
return len ? hdr->data : NULL;
}
void enc_plmn_search_res(srslte::byte_buffer_t& buf,
const srsue::rrc_interface_nas::found_plmn_t* plmns,
size_t nof_plmns)
{
struct proto::msg_plmn_search_res msg;
uint16_t msg_len;
if (nof_plmns > 16)
throw codec::error("too many PLMNS to encode");
msg.nof_plmns = static_cast<uint8_t> (nof_plmns);
for (size_t i = 0; i < nof_plmns; i++) {
plmns[i].plmn_id.to_rrctl_bytes(msg.plmns[i].mcc, msg.plmns[i].mnc);
msg.plmns[i].tac = htons(plmns[i].tac);
}
msg_len = sizeof(proto::msg_plmn_search_res::plmn) * nof_plmns + 1;
enc_hdr(buf, proto::RRCTL_PLMN_SEARCH, proto::RRCTL_CNF, msg_len);
buf.append_bytes((uint8_t *) &msg, msg_len);
}
void dec_plmn_select_req(std::pair<uint16_t, uint16_t>& mcc_mnc,
const uint8_t* payload, size_t len)
{
const struct proto::msg_plmn_select_req* msg;
struct srslte::plmn_id_t plmn_id;
if (len < sizeof(*msg))
throw codec::error("body is too short");
msg = reinterpret_cast<const struct proto::msg_plmn_select_req*> (payload);
plmn_id.from_rrctl_bytes(msg->mcc, msg->mnc);
mcc_mnc = plmn_id.to_number();
}
void dec_conn_establish_req(srslte::establishment_cause_t& cause,
const uint8_t*& pdu, size_t& pdu_len,
const uint8_t* payload, size_t len)
{
const struct proto::msg_conn_establish_req* msg;
if (len < sizeof(*msg))
throw codec::error("body is too short");
msg = reinterpret_cast<const struct proto::msg_conn_establish_req*> (payload);
cause = static_cast<srslte::establishment_cause_t> (msg->cause);
pdu_len = len - 1;
pdu = msg->pdu;
}
void enc_data_ind(srslte::byte_buffer_t& buf,
const uint8_t *pdu, size_t pdu_len,
uint32_t lcid)
{
struct proto::msg_data msg;
msg.lcid = htonl(lcid);
enc_hdr(buf, proto::RRCTL_DATA, proto::RRCTL_IND, sizeof(msg) + pdu_len);
buf.append_bytes((const uint8_t*) &msg, sizeof(msg));
buf.append_bytes(pdu, pdu_len);
}
void enc_paging_ind(srslte::byte_buffer_t& buf,
srslte::s_tmsi_t* ue_identity)
{
struct proto::msg_paging_ind msg;
msg.ueid.m_tmsi = htonl(ue_identity->m_tmsi);
msg.ueid.mmec = ue_identity->mmec;
enc_hdr(buf, proto::RRCTL_PAGING, proto::RRCTL_IND, sizeof(msg));
buf.append_bytes((const uint8_t*) &msg, sizeof(msg));
}
} // namespace codec
} // namespace rrctl

View File

@ -231,11 +231,11 @@ int security_command_test()
usim.init(&args);
{
srsue::nas nas(&stack.task_sched);
nas_args_t cfg;
cfg.eia = "1,2,3";
cfg.eea = "0,1,2,3";
nas.init(&usim, &rrc_dummy, &gw, cfg);
srsue::nas nas(&stack.task_sched, cfg);
nas.init(&usim, &rrc_dummy, &gw);
rrc_dummy.init(&nas);
// push auth request PDU to NAS to generate security context
@ -299,10 +299,10 @@ int mme_attach_request_test()
nas_cfg.apn_name = "test123";
test_stack_dummy stack(&pdcp_dummy);
srsue::nas nas(&stack.task_sched);
srsue::nas nas(&stack.task_sched, nas_cfg);
srsue::gw gw;
nas.init(&usim, &rrc_dummy, &gw, nas_cfg);
nas.init(&usim, &rrc_dummy, &gw);
rrc_dummy.init(&nas);
gw_args_t gw_args;
@ -377,13 +377,13 @@ int esm_info_request_test()
pool = byte_buffer_pool::get_instance();
{
srsue::nas nas(&stack.task_sched);
nas_args_t cfg;
cfg.apn_name = "srslte";
cfg.apn_user = "srsuser";
cfg.apn_pass = "srspass";
cfg.force_imsi_attach = true;
nas.init(&usim, &rrc_dummy, &gw, cfg);
srsue::nas nas(&stack.task_sched, cfg);
nas.init(&usim, &rrc_dummy, &gw);
// push ESM info request PDU to NAS to generate response
unique_byte_buffer_t tmp = srslte::allocate_unique_buffer(*pool, true);
@ -427,10 +427,10 @@ int dedicated_eps_bearer_test()
srslte::byte_buffer_pool* pool = byte_buffer_pool::get_instance();
srsue::nas nas(&stack.task_sched);
nas_args_t cfg = {};
cfg.force_imsi_attach = true; // make sure we get a fresh security context
nas.init(&usim, &rrc_dummy, &gw, cfg);
srsue::nas nas(&stack.task_sched, cfg);
nas.init(&usim, &rrc_dummy, &gw);
// push dedicated EPS bearer PDU to NAS
unique_byte_buffer_t tmp = srslte::allocate_unique_buffer(*pool, true);

View File

@ -173,7 +173,7 @@ public:
class nas_test : public srsue::nas
{
public:
nas_test(srslte::task_sched_handle t) : srsue::nas(t) {}
nas_test(srslte::task_sched_handle t) : srsue::nas(t, {}) {}
bool is_attached() override { return false; }
};

View File

@ -163,6 +163,16 @@ imei = 353490069873319
#eia = 1,2
#eea = 0,1,2
#####################################################################
# External NAS interface configuration
#
# enable: Disable the built-in NAS implementation, provide external interface
# sock_path: UNIX socket path of the external interface
#####################################################################
[extnas]
#enable = false
#sock_path = /tmp/ue_extnas.sock
#####################################################################
# GW configuration
#