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.
This commit is contained in:
Vadim Yanitskiy 2020-03-12 05:23:57 +07:00
parent 8bc72d44b9
commit 900ad92912
17 changed files with 330 additions and 14 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

@ -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 {

View File

@ -153,6 +153,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;

View File

@ -35,6 +35,7 @@
#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"
using srslte::byte_buffer_t;
@ -75,6 +76,9 @@ public:
private:
nas_ext_args_t cfg = {};
// Interface to an external NAS entity
std::unique_ptr<nas_extif_base> iface;
};
} // namespace srsue

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

@ -166,6 +166,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 +537,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 +571,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

@ -102,6 +102,8 @@ 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
// TODO: or provide an external interface (RRCTL)?

View File

@ -18,7 +18,7 @@
# and at http://www.gnu.org/licenses/.
#
set(SOURCES gw.cc nas.cc nas_ext.cc usim_base.cc usim.cc tft_packet_filter.cc)
set(SOURCES gw.cc nas.cc nas_ext.cc nas_extif.cc usim_base.cc usim.cc tft_packet_filter.cc)
if(HAVE_PCSC)
list(APPEND SOURCES "pcsc_usim.cc")

View File

@ -32,6 +32,7 @@
#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;
@ -44,8 +45,12 @@ void nas_ext::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interf
rrc = rrc_;
gw = gw_;
// TODO: parse the configuration
// TODO: init the UNIX domain socket
auto rx_cb = [this](const srslte::byte_buffer_t& pdu) {
// TODO: parse received payload
};
std::unique_ptr<nas_extif_unix> iface_(new nas_extif_unix(rx_cb, cfg.sock_path));
iface = std::move(iface_);
}
void nas_ext::get_metrics(nas_metrics_t* m)
@ -57,7 +62,9 @@ void nas_ext::get_metrics(nas_metrics_t* m)
void nas_ext::stop()
{
// TODO: close the UNIX domain socket connection
// Close the UNIX domain socket connection
iface->close();
iface.release();
}
/*******************************************************************************

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