added pnf_dummy test

master
Francisco Paisana 5 years ago
parent 8a022fb35b
commit b05ce2fe57

@ -0,0 +1,428 @@
/*
* 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/.
*
*/
#ifndef SRSLTE_BASIC_PNF_H
#define SRSLTE_BASIC_PNF_H
#include "basic_vnf_api.h"
#include "common.h"
#include <arpa/inet.h>
#include <atomic>
#include <errno.h>
#include <fcntl.h>
#include <iostream>
#include <mutex>
#include <netinet/in.h>
#include <poll.h>
#include <random>
#include <strings.h>
#include <sys/socket.h>
#include <thread>
#include <unistd.h>
#define RAND_SEED (12384)
#define RX_TIMEOUT_MS (500)
#define MIN_TB_LEN (100) // MAX_TB_LEN defined in api.h
namespace srslte {
struct pnf_metrics_t {
uint32_t avg_rtt_us;
uint32_t num_timing_errors;
uint32_t num_pdus;
uint32_t tb_size;
};
class srslte_basic_pnf
{
public:
srslte_basic_pnf(const std::string& type_,
const std::string& vnf_p5_addr,
const uint16_t& vnf_p5_port,
const uint32_t& sf_interval,
const int32_t& num_sf_,
const uint32_t& tb_len_) :
running(false),
type(type_),
vnf_addr(vnf_p5_addr),
vnf_port(vnf_p5_port),
sf_interval_us(sf_interval),
num_sf(num_sf_),
tb_len(tb_len_),
rand_gen(RAND_SEED),
rand_dist(MIN_TB_LEN, MAX_TB_LEN){};
~srslte_basic_pnf() { stop(); };
bool start()
{
// create socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return false;
}
int enable = 1;
#if defined(SO_REUSEADDR)
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
}
#endif
#if defined(SO_REUSEPORT)
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
perror("setsockopt(SO_REUSEPORT) failed");
}
#endif
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(vnf_addr.c_str());
servaddr.sin_port = htons(vnf_port);
// start main thread
running = true;
if (type == "gnb") {
rx_thread = std::unique_ptr<std::thread>(new std::thread(&srslte_basic_pnf::rx_thread_function, this));
tx_thread = std::unique_ptr<std::thread>(new std::thread(&srslte_basic_pnf::tx_thread_function, this));
} else {
tx_thread = std::unique_ptr<std::thread>(new std::thread(&srslte_basic_pnf::tx_thread_function_ue, this));
}
return true;
};
bool stop()
{
running = false;
if (rx_thread) {
if (rx_thread->joinable()) {
rx_thread->join();
}
}
if (tx_thread) {
if (tx_thread->joinable()) {
tx_thread->join();
}
}
return true;
};
pnf_metrics_t get_metrics()
{
pnf_metrics_t tmp = metrics;
metrics = {};
return tmp;
}
private:
void rx_thread_function()
{
pthread_setname_np(pthread_self(), rx_thread_name.c_str());
// set_rt_prio();
struct pollfd fd;
fd.fd = sockfd;
fd.events = POLLIN;
const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_conf_msg_t) + 32; // larger than biggest message
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> > rx_buffer =
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> >(new std::array<uint8_t, max_basic_api_pdu>);
while (running) {
// receive response
int ret = poll(&fd, 1, RX_TIMEOUT_MS);
switch (ret) {
case -1:
printf("Error occured.\n");
running = false;
break;
case 0:
// Timeout
printf("Error: Didn't receive response after %dms\n", RX_TIMEOUT_MS);
running = false;
break;
default:
int recv_ret = recv(sockfd, rx_buffer->data(), rx_buffer->size(), 0);
handle_msg(rx_buffer->data(), recv_ret);
break;
}
std::lock_guard<std::mutex> lock(mutex);
auto rtt =
std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - tti_start_time)
.count();
// FIXME: add averaging
metrics.avg_rtt_us = rtt;
}
};
void tx_thread_function()
{
pthread_setname_np(pthread_self(), tx_thread_name.c_str());
// set_rt_prio();
struct pollfd fd;
fd.fd = sockfd;
fd.events = POLLIN;
const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_conf_msg_t) + 32; // larger than biggest message
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> > rx_buffer =
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> >(new std::array<uint8_t, max_basic_api_pdu>);
int32_t sf_counter = 0;
while (running && (num_sf > 0 ? sf_counter < num_sf : true)) {
{
std::lock_guard<std::mutex> lock(mutex);
// Increase TTI
tti = (tti + 1) % 10240;
// Take time before sending the SF indication
tti_start_time = std::chrono::steady_clock::now();
// Send request
send_sf_ind(tti);
sf_counter++;
}
std::this_thread::sleep_for(std::chrono::microseconds(sf_interval_us));
}
printf("Leaving Tx thread after %d subframes\n", sf_counter);
};
void tx_thread_function_ue()
{
pthread_setname_np(pthread_self(), tx_thread_name.c_str());
// set_rt_prio();
struct pollfd fd;
fd.fd = sockfd;
fd.events = POLLIN;
const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_conf_msg_t) + 32; // larger than biggest message
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> > rx_buffer =
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> >(new std::array<uint8_t, max_basic_api_pdu>);
int32_t sf_counter = 0;
while (running && (num_sf > 0 ? sf_counter < num_sf : true)) {
{
std::lock_guard<std::mutex> lock(mutex);
// Increase TTI
tti = (tti + 1) % 10240;
// Take time before sending the SF indication
tti_start_time = std::chrono::steady_clock::now();
// Send SF indication
send_sf_ind(tti);
// provide DL grant every even TTI, and UL grant every odd
if (tti % 2 == 0) {
send_dl_ind(tti);
} else {
send_ul_ind(tti);
}
sf_counter++;
}
std::this_thread::sleep_for(std::chrono::microseconds(sf_interval_us));
}
printf("Leaving Tx thread after %d subframes\n", sf_counter);
};
void send_sf_ind(uint32_t tti_)
{
basic_vnf_api::sf_ind_msg_t sf_ind;
bzero(&sf_ind, sizeof(sf_ind));
sf_ind.header.type = basic_vnf_api::SF_IND;
sf_ind.header.msg_len = sizeof(sf_ind) - sizeof(basic_vnf_api::msg_header_t);
sf_ind.tti = tti_;
sf_ind.t1 = 0;
sf_ind.tb_len = tb_len > 0 ? tb_len : rand_dist(rand_gen);
int n = 0;
if ((n = sendto(sockfd, &sf_ind, sizeof(sf_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) {
printf("sendto failed, ret=%d\n", n);
}
}
int handle_msg(const uint8_t* buffer, const uint32_t len)
{
basic_vnf_api::msg_header_t* header = (basic_vnf_api::msg_header_t*)buffer;
// printf("Received %s (%d B) in TTI\n", msg_type_text[header->type], len);
switch (header->type) {
case basic_vnf_api::SF_IND:
printf("Error: %s not handled by VNF\n", basic_vnf_api::msg_type_text[header->type]);
break;
case basic_vnf_api::DL_CONFIG:
handle_dl_config((basic_vnf_api::dl_conf_msg_t*)header);
break;
case basic_vnf_api::TX_REQUEST:
handle_tx_request((basic_vnf_api::tx_request_msg_t*)header);
break;
default:
printf("Unknown msg type.\n");
break;
}
return 0;
}
int handle_dl_config(basic_vnf_api::dl_conf_msg_t* msg)
{
// printf("Received DL config for TTI=%d\n", msg->tti);
if (msg->tti != tti) {
metrics.num_timing_errors++;
// printf("Received DL config for TTI=%d but current TTI is %d\n", msg->tti, tti.load());
return -1;
}
return 0;
}
int handle_tx_request(basic_vnf_api::tx_request_msg_t* msg)
{
// printf("Received TX request config for TTI=%d\n", msg->tti);
if (msg->tti != tti) {
metrics.num_timing_errors++;
// printf("Received TX request for TTI=%d but current TTI is %d\n", msg->tti, tti.load());
return -1;
}
for (uint32_t i = 0; i < msg->nof_pdus; ++i) {
metrics.tb_size += msg->pdus[i].length;
}
metrics.num_pdus += msg->nof_pdus;
return 0;
}
void send_dl_ind(uint32_t tti_)
{
// MAC PDU with a single LCID with padding only
static uint8_t tv[] = {
0x01,
0x08,
0x11,
0x22,
0x33,
0x44,
0x55,
0x66,
0x77,
0x88,
0x3f,
};
basic_vnf_api::dl_ind_msg_t dl_ind = {};
dl_ind.header.type = basic_vnf_api::DL_IND;
dl_ind.header.msg_len = sizeof(dl_ind) - sizeof(basic_vnf_api::msg_header_t);
dl_ind.tti = tti_;
dl_ind.t1 = 0;
dl_ind.nof_pdus = 1;
dl_ind.pdus[0].type = basic_vnf_api::PDSCH;
dl_ind.pdus[0].length = tb_len > 0 ? tb_len : rand_dist(rand_gen);
if (dl_ind.pdus[0].length >= sizeof(tv)) {
// copy TV
memcpy(dl_ind.pdus[0].data, tv, sizeof(tv));
// set remaining bytes to zero
memset(dl_ind.pdus[0].data + sizeof(tv), 0xaa, dl_ind.pdus[0].length - sizeof(tv));
} else {
// just fill with dummy bytes
memset(dl_ind.pdus[0].data, 0xab, dl_ind.pdus[0].length);
}
int n = 0;
if ((n = sendto(sockfd, &dl_ind, sizeof(dl_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) {
printf("sendto failed, ret=%d\n", n);
}
}
void send_ul_ind(uint32_t tti_)
{
basic_vnf_api::ul_ind_msg_t ul_ind = {};
ul_ind.header.type = basic_vnf_api::UL_IND;
ul_ind.header.msg_len = sizeof(ul_ind) - sizeof(basic_vnf_api::msg_header_t);
ul_ind.tti = tti_;
ul_ind.t1 = 0;
ul_ind.pdus.type = basic_vnf_api::PUSCH;
ul_ind.pdus.length = tb_len > 0 ? tb_len : rand_dist(rand_gen);
int n = 0;
if ((n = sendto(sockfd, &ul_ind, sizeof(ul_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) {
printf("sendto failed, ret=%d\n", n);
}
}
std::unique_ptr<std::thread> tx_thread, rx_thread;
std::string tx_thread_name = "TX_PNF", rx_thread_name = "RX_PNF";
bool running = false;
std::mutex mutex;
std::atomic<std::uint32_t> tti;
std::chrono::steady_clock::time_point tti_start_time;
std::string type;
std::string vnf_addr;
uint16_t vnf_port = 3333;
uint32_t sf_interval_us = 1000;
int32_t num_sf = -1;
uint32_t tb_len = 100;
pnf_metrics_t metrics = {};
int sockfd = 0;
struct sockaddr_in servaddr = {};
// For random number generation
std::mt19937 rand_gen;
std::uniform_int_distribution<uint16_t> rand_dist;
};
} // namespace srslte
#endif // SRSLTE_BASIC_PNF_H

@ -0,0 +1,94 @@
/*
* 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/.
*
*/
#ifndef SRSLTE_BASIC_VNF_H
#define SRSLTE_BASIC_VNF_H
#include "basic_vnf_api.h"
#include "common.h"
#include "srslte/common/log_filter.h"
#include "srslte/common/threads.h"
#include "srslte/interfaces/gnb_interfaces.h"
#include "srslte/interfaces/ue_nr_interfaces.h"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <iostream>
#include <netinet/in.h>
#include <random>
#include <strings.h>
#include <sys/socket.h>
#include <thread>
#include <unistd.h>
namespace srslte {
class srslte_basic_vnf : public thread
{
public:
srslte_basic_vnf(const vnf_args_t& args_, srslte::logger* logger_, stack_interface_phy_nr* stack_);
~srslte_basic_vnf();
bool stop();
int dl_config_request(const srsenb::phy_interface_stack_nr::dl_config_request_t& request);
int tx_request(const srsenb::phy_interface_stack_nr::tx_request_t& request);
int tx_request(const srsue::phy_interface_stack_nr::tx_request_t& request);
private:
void run_thread();
// handlers
int handle_msg(const uint8_t* buffer, const uint32_t len);
int handle_sf_ind(basic_vnf_api::sf_ind_msg_t* msg);
int handle_dl_ind(basic_vnf_api::dl_ind_msg_t* msg);
int handle_ul_ind(basic_vnf_api::ul_ind_msg_t* msg);
// senders
int send_dl_config_request();
// helpers
uint32_t calc_full_msg_len(const basic_vnf_api::tx_request_msg_t& msg);
srslte::logger* m_logger = nullptr;
std::unique_ptr<srslte::log_filter> m_log = nullptr;
srsenb::stack_interface_phy_nr* m_gnb_stack = nullptr;
srsue::stack_interface_phy_nr* m_ue_stack = nullptr;
srslte::byte_buffer_pool* m_pool = nullptr;
std::unique_ptr<basic_vnf_api::tx_request_msg_t> m_tx_req_msg;
// std::unique_ptr<srsue::stack_interface_phy_nr::mac_nr_grant_dl_t> m_dl_grant; ///< Used by UE as temp buffer for
// all DL indications
bool running = false;
vnf_args_t m_args = {};
int sockfd = 0;
struct sockaddr_in servaddr = {}, client_addr = {};
uint32_t last_sf_indication_time = 0;
};
} // namespace srslte
#endif // SRSLTE_BASIC_VNF_H

@ -0,0 +1,147 @@
/*
* 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/.
*
*/
#ifndef SRSLTE_BASIC_VNF_API_H
#define SRSLTE_BASIC_VNF_API_H
#include <thread>
namespace basic_vnf_api {
// PNF (the PHY) VNF (the L2/L3)
// | |
// | |
// | - |
// | \ sf_ind_msg_t
// | \ |
// | \ |
// | \ |
// | \ |
// | \ |
// | \|
// | |
// | | DL_CONFIG.request
// | /|
// | / |
// | / |
// | / |
// | / |
// | / dl_conf_msg_t
// | / |
// |/ | TX.request
// | /|
// | / |
// | / |
// | / |
// | / |
// | / tx_request_msg_t
// | / |
// |/ |
// | |
// Primitive API messages for basic testing basic VNF/PNF interaction
enum msg_type_t { SF_IND, DL_CONFIG, TX_REQUEST, DL_IND, UL_IND, MSG_TYPE_NITEMS };
static const char* msg_type_text[MSG_TYPE_NITEMS] = {"SF Indication",
"DL_CONFIG.Request",
"TX.Request",
"DL_Indication",
"UL_Indication"};
enum pdu_type_t { MAC_PBCH, PHY_PBCH, PDCCH, PDSCH, PUSCH };
struct msg_header_t {
msg_type_t type;
uint32_t msg_len;
};
struct sf_ind_msg_t {
msg_header_t header;
uint32_t t1; // Timestamp taken at PNF
uint32_t tti; // TTI of requested subframe
uint32_t tb_len; // Length of the TB
};
#define MAX_TB_LEN (16 * 1024)
#define MAX_PDU_SIZE (16 * 1024)
#define MAX_NUM_PDUS (1)
struct phy_pbch_pdu_t {
uint16_t phy_cell_id; // 0 - 1007
uint8_t ss_block_index; // 0-63
uint8_t ssb_sc_offset; // 0-15
uint8_t dmrs_pos; // 0-1
uint8_t pdcch_config; // 0-255
};
struct dl_conf_msg_t {
msg_header_t header;
uint32_t t1; // Replayed timestamp
uint32_t t2; // Timestamp taken at VNF
uint32_t tti; // TTI
uint16_t beam_id; // tx beam id for the slot
};
struct tx_request_pdu_t {
uint16_t length;
uint16_t index; // index indicated in dl_config
pdu_type_t type; // physical chan of pdu/tb
uint8_t data[MAX_PDU_SIZE];
};
struct tx_request_msg_t {
msg_header_t header;
uint32_t tti; // TTI
uint32_t tb_len; // actual TB len
uint32_t nof_pdus;
tx_request_pdu_t pdus[MAX_NUM_PDUS];
};
// UE specific messages
struct dl_ind_pdu_t {
pdu_type_t type; // physical chan of pdu/tb
uint16_t length;
uint8_t data[MAX_PDU_SIZE];
};
struct dl_ind_msg_t {
msg_header_t header;
uint32_t t1; // Timestamp taken at PNF
uint32_t tti; // tti or slot?
uint32_t nof_pdus;
dl_ind_pdu_t pdus[MAX_NUM_PDUS];
};
///< Messages for UL (only one PDU)
struct ul_ind_pdu_t {
pdu_type_t type; // physical chan of pdu/tb
uint16_t length;
};
struct ul_ind_msg_t {
msg_header_t header;
uint32_t t1; // Timestamp taken at PNF
uint32_t tti; // tti or slot?
uint32_t rnti; ///< RNTI of this grant
ul_ind_pdu_t pdus;
};
} // namespace basic_vnf_api
#endif // SRSLTE_BASIC_VNF_API_H

@ -64,6 +64,14 @@ typedef struct {
} rf_args_t;
struct vnf_args_t {
std::string type;
std::string bind_addr;
uint16_t bind_port;
std::string log_level;
int log_hex_limit;
};
class srslte_gw_config_t
{
public:

@ -44,7 +44,7 @@ set(SOURCES arch_select.cc
s3g.cc)
if (ENABLE_5GNR)
set(SOURCES ${SOURCES} mac_nr_pcap.cc)
set(SOURCES ${SOURCES} mac_nr_pcap.cc basic_vnf.cc)
endif(ENABLE_5GNR)
# Avoid warnings caused by libmbedtls about deprecated functions
@ -59,4 +59,4 @@ add_executable(arch_select arch_select.cc)
target_include_directories(srslte_common PUBLIC ${SEC_INCLUDE_DIRS})
target_link_libraries(srslte_common srslte_phy ${SEC_LIBRARIES})
add_subdirectory(test)
add_subdirectory(test)

@ -0,0 +1,365 @@
/*
* 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 "srslte/common/basic_vnf.h"
#include "srslte/interfaces/ue_nr_interfaces.h"
#include <algorithm>
#include <chrono>
#include <poll.h>
#define RAND_SEED (12314)
#define RX_TIMEOUT_MS (1000)
namespace srslte {
struct srslte_pnf_info_t {
// TODO: fill when needed
};
struct srslte_vnf_info_t {};
srslte_basic_vnf::srslte_basic_vnf(const vnf_args_t& args_, srslte::logger* logger_, stack_interface_phy_nr* stack_) :
m_args(args_),
m_logger(logger_),
thread("BASIC_VNF_P7"),
m_tx_req_msg(new basic_vnf_api::tx_request_msg_t),
m_log(new srslte::log_filter),
m_pool(srslte::byte_buffer_pool::get_instance())
{
m_log->init("VNF ", m_logger);
m_log->set_level(m_args.log_level);
m_log->set_hex_limit(m_args.log_hex_limit);
if (m_args.type == "gnb" || m_args.type == "ue") {
if (m_args.type == "gnb") {
m_gnb_stack = (srsenb::stack_interface_phy_nr*)stack_;
} else {
m_ue_stack = (srsue::stack_interface_phy_nr*)stack_;
}
m_log->info("Initializing VNF for gNB\n");
start();
} else {
m_log->error("Unknown VNF type. Exiting\n.");
}
}
srslte_basic_vnf::~srslte_basic_vnf()
{
stop();
}
void srslte_basic_vnf::run_thread()
{
// Bind to UDP socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return;
}
// Make sockets reusable
int enable = 1;
#if defined(SO_REUSEADDR)
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
}
#endif
#if defined(SO_REUSEPORT)
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
perror("setsockopt(SO_REUSEPORT) failed");
}
#endif
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(m_args.bind_port);
if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in))) {
perror("bind");
return;
}
struct pollfd fd;
fd.fd = sockfd;
fd.events = POLLIN;
const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_ind_msg_t) + 32; // larger than biggest message
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> > rx_buffer =
std::unique_ptr<std::array<uint8_t, max_basic_api_pdu> >(new std::array<uint8_t, max_basic_api_pdu>);
running = true;
m_log->info("Started VNF handler listening on %s:%d\n", m_args.bind_addr.c_str(), m_args.bind_port);
while (running) {
int ret = poll(&fd, 1, RX_TIMEOUT_MS);
switch (ret) {
case -1:
printf("Error occured.\n");
break;
case 0:
// Timeout
break;
default:
socklen_t len = sizeof(client_addr);
ret = recvfrom(sockfd, rx_buffer->data(), rx_buffer->size(), MSG_WAITALL, (struct sockaddr*)&client_addr, &len);
handle_msg(rx_buffer->data(), ret);
break;
}
}
m_log->info("VNF thread stopped\n");
}
int srslte_basic_vnf::handle_msg(const uint8_t* buffer, const uint32_t len)
{
basic_vnf_api::msg_header_t* header = (basic_vnf_api::msg_header_t*)buffer;
m_log->info("Received %s (%d B)\n", basic_vnf_api::msg_type_text[header->type], len);
switch (header->type) {
case basic_vnf_api::SF_IND:
handle_sf_ind((basic_vnf_api::sf_ind_msg_t*)header);
break;
case basic_vnf_api::DL_CONFIG:
printf("Error: %s not handled by VNF\n", basic_vnf_api::msg_type_text[header->type]);
break;
case basic_vnf_api::DL_IND:
handle_dl_ind((basic_vnf_api::dl_ind_msg_t*)header);
break;
case basic_vnf_api::UL_IND:
handle_ul_ind((basic_vnf_api::ul_ind_msg_t*)header);
break;
default:
printf("Unknown msg type.\n");
break;
}
return 0;
}
int srslte_basic_vnf::handle_sf_ind(basic_vnf_api::sf_ind_msg_t* msg)
{
int ret = SRSLTE_SUCCESS;
m_log->info("Received %s for TTI=%d\n", basic_vnf_api::msg_type_text[msg->header.type], msg->tti);
// store Rx timestamp
last_sf_indication_time = msg->t1;
if (m_gnb_stack != nullptr) {
m_gnb_stack->sf_indication(msg->tti);
} else if (m_ue_stack != nullptr) {
m_ue_stack->sf_indication(msg->tti);
} else {
ret = SRSLTE_ERROR;
}
return ret;
}
int srslte_basic_vnf::handle_dl_ind(basic_vnf_api::dl_ind_msg_t* msg)
{
int ret = SRSLTE_ERROR;
m_log->info("Received %s for TTI=%d\n", basic_vnf_api::msg_type_text[msg->header.type], msg->tti);
uint32_t cc_idx = 0;
// fill DL struct
srsue::stack_interface_phy_nr::mac_nr_grant_dl_t dl_grant = {};
dl_grant.tti = msg->tti;
if (msg->nof_pdus > SRSLTE_MAX_TB) {
m_log->error("Too many TBs (%d > %d)\n", msg->nof_pdus, SRSLTE_MAX_TB);
goto exit;
}
for (uint32_t i = 0; i < msg->nof_pdus; ++i) {
dl_grant.tb[i] = srslte::allocate_unique_buffer(*m_pool);
if (dl_grant.tb[i]->get_tailroom() >= msg->pdus[i].length) {
memcpy(dl_grant.tb[i]->msg, msg->pdus[i].data, msg->pdus[i].length);
dl_grant.tb[i]->N_bytes = msg->pdus[i].length;
if (msg->pdus[i].type == basic_vnf_api::PDSCH) {
m_ue_stack->tb_decoded(cc_idx, dl_grant);
}
} else {
m_log->error("TB too big to fit into buffer (%d > %d)\n", msg->pdus[i].length, dl_grant.tb[i]->get_tailroom());
}
}
ret = SRSLTE_SUCCESS;
exit:
return ret;
}
int srslte_basic_vnf::handle_ul_ind(basic_vnf_api::ul_ind_msg_t* msg)
{
m_log->info("Received %s for TTI=%d\n", basic_vnf_api::msg_type_text[msg->header.type], msg->tti);
if (msg->pdus.type != basic_vnf_api::PUSCH) {
m_log->error("Received UL indication for wrong PDU type\n");
return SRSLTE_ERROR;
}
uint32_t cc_idx = 0;
// fill DL struct
srsue::stack_interface_phy_nr::mac_nr_grant_ul_t ul_grant = {};
ul_grant.tti = msg->tti;
ul_grant.tbs = msg->pdus.length;
ul_grant.rnti = msg->rnti;
m_ue_stack->new_grant_ul(cc_idx, ul_grant);
return SRSLTE_SUCCESS;
}
int srslte_basic_vnf::dl_config_request(const srsenb::phy_interface_stack_nr::dl_config_request_t& request)
{
// Generate DL Config
basic_vnf_api::dl_conf_msg_t dl_conf = {};
dl_conf.header.type = basic_vnf_api::DL_CONFIG;
dl_conf.header.msg_len = sizeof(dl_conf) - sizeof(basic_vnf_api::msg_header_t);
dl_conf.t1 = last_sf_indication_time; // play back the time
dl_conf.t2 = 0xaa; // FIXME: add timestamp
dl_conf.tti = request.tti;
dl_conf.beam_id = request.beam_id;
// Send entire struct
uint32_t len = sizeof(dl_conf);
// Send it to PNF
m_log->info("Sending %s (%d B)\n", basic_vnf_api::msg_type_text[dl_conf.header.type], len);
int n = 0;
if ((n = sendto(sockfd, &dl_conf, len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) < 0) {
m_log->error("sendto failed, ret=%d\n", n);
}
return 0;
}
/// Tx request from UE, i.e. UL transmission
int srslte_basic_vnf::tx_request(const srsue::phy_interface_stack_nr::tx_request_t& request)
{
// Generate Tx request
m_tx_req_msg->header.type = basic_vnf_api::TX_REQUEST;
m_tx_req_msg->header.msg_len = 0; // set further down
m_tx_req_msg->tti = request.tti;
m_tx_req_msg->nof_pdus = 1;
m_tx_req_msg->pdus[0].index = 0;
m_tx_req_msg->pdus[0].type = basic_vnf_api::PUSCH;
m_tx_req_msg->pdus[0].length = request.tb_len;
if (request.tb_len <= MAX_PDU_SIZE) {
// copy data from TB0
memcpy(m_tx_req_msg->pdus[0].data, request.data, request.tb_len);
} else {
m_log->error("Trying to send %d B PDU. Maximum size is %d B\n", request.tb_len, MAX_PDU_SIZE);
}
// calculate actual length of
uint32_t len = calc_full_msg_len(*m_tx_req_msg.get());
// update msg header length field
m_tx_req_msg->header.msg_len = len - sizeof(basic_vnf_api::msg_header_t);
// Send it to PNF
m_log->info("Sending %s (%d B)\n", basic_vnf_api::msg_type_text[m_tx_req_msg->header.type], len);
int n = 0;
if ((n = sendto(sockfd, m_tx_req_msg.get(), len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) <
0) {
m_log->error("sendto failed, ret=%d\n", n);
}
return 0;
}
int srslte_basic_vnf::tx_request(const srsenb::phy_interface_stack_nr::tx_request_t& request)
{
if (request.nof_pdus > MAX_NUM_PDUS) {
m_log->error("Trying to send %d PDUs but only %d supported\n", request.nof_pdus, MAX_NUM_PDUS);
return SRSLTE_ERROR;
}
// Generate Tx request
m_tx_req_msg->header.type = basic_vnf_api::TX_REQUEST;
m_tx_req_msg->header.msg_len = 0; // set further down
m_tx_req_msg->nof_pdus = request.nof_pdus;
m_tx_req_msg->tti = request.tti;
for (uint32_t i = 0; i < m_tx_req_msg->nof_pdus; ++i) {
if (request.pdus[i].length <= MAX_PDU_SIZE) {
m_tx_req_msg->pdus[i].index = i;
m_tx_req_msg->pdus[i].type = request.pdus[i].pbch.mib_present ? basic_vnf_api::MAC_PBCH : basic_vnf_api::PDSCH;
m_tx_req_msg->pdus[i].length = request.pdus[i].length;
// copy data from TB0
memcpy(m_tx_req_msg->pdus[i].data, request.pdus[i].data[0], m_tx_req_msg->pdus[i].length);
} else {
m_log->error("Trying to send %d B PDU. Maximum size is %d B\n", request.pdus[i].length, MAX_PDU_SIZE);
}
}
// calculate actual length of message
uint32_t len = calc_full_msg_len(*m_tx_req_msg.get());
// update msg header length field
m_tx_req_msg->header.msg_len = len - sizeof(basic_vnf_api::msg_header_t);
// Send it to PNF
m_log->info("Sending %s (%d B)\n", basic_vnf_api::msg_type_text[m_tx_req_msg->header.type], len);
int n = 0;
if ((n = sendto(sockfd, m_tx_req_msg.get(), len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) <
0) {
m_log->error("sendto failed, ret=%d\n", n);
}
return 0;
}
uint32_t srslte_basic_vnf::calc_full_msg_len(const basic_vnf_api::tx_request_msg_t& msg)
{
// start with mandatory part
uint32_t len = sizeof(basic_vnf_api::msg_header_t) + 3 * sizeof(uint32_t);
// add all PDUs
for (uint32_t i = 0; i < msg.nof_pdus; ++i) {
len += 2 * sizeof(uint16_t) + sizeof(basic_vnf_api::pdu_type_t) + msg.pdus[i].length;
}
return len;
}
bool srslte_basic_vnf::stop()
{
if (running) {
running = false;
wait_thread_finish();
}
return true;
}
} // namespace srslte

@ -97,3 +97,7 @@ add_test(choice_type_test choice_type_test)
add_executable(expected_test expected_test.cc)
target_link_libraries(expected_test srslte_common)
add_test(expected_test expected_test)
add_executable(pnf_dummy pnf_dummy.cc)
target_link_libraries(pnf_dummy ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})

@ -0,0 +1,117 @@
/*
* 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 <assert.h>
#include <boost/program_options.hpp>
#include <boost/program_options/parsers.hpp>
#include <chrono>
#include <iostream>
#include <signal.h>
#include <thread>
#include "srslte/common/basic_pnf.h"
using namespace std;
namespace bpo = boost::program_options;
struct pnf_args_t {
std::string type;
std::string vnf_addr;
uint16_t vnf_port;
uint32_t sf_interval;
int32_t num_sf;
uint32_t tb_len;
};
void parse_args(pnf_args_t* args, int argc, char* argv[])
{
// Command line only options
bpo::options_description general("General options");
general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit");
// Command line or config file options
bpo::options_description common("Configuration options");
// clang-format off
common.add_options()
("vnf.type", bpo::value<string>(&args->type)->default_value("gnb"), "VNF instance type [gnb,ue]")
("vnf.addr", bpo::value<string>(&args->vnf_addr)->default_value("127.0.0.1"), "VNF address")
("vnf.port", bpo::value<uint16_t>(&args->vnf_port)->default_value(3333), "VNF port")
("sf_interval", bpo::value<uint32_t>(&args->sf_interval)->default_value(1000), "Interval between subframes in us")
("num_sf", bpo::value<int32_t>(&args->num_sf)->default_value(-1), "Number of subframes to signal (-1 infinity)")
("tb_len", bpo::value<uint32_t>(&args->tb_len)->default_value(0), "TB lenth (0 for random size)");
// clang-format on
// these options are allowed on the command line
bpo::options_description cmdline_options;
cmdline_options.add(common).add(general);
// parse the command line and store result in vm
bpo::variables_map vm;
bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm);
bpo::notify(vm);
// help option was given - print usage and exit
if (vm.count("help")) {
cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl;
cout << common << endl << general << endl;
exit(0);
}
}
bool running = true;
void sig_int_handler(int signo)
{
printf("SIGINT received. Exiting...\n");
if (signo == SIGINT) {
running = false;
}
}
int main(int argc, char** argv)
{
signal(SIGINT, sig_int_handler);
pnf_args_t args;
parse_args(&args, argc, argv);
srslte::srslte_basic_pnf pnf(args.type, args.vnf_addr, args.vnf_port, args.sf_interval, args.num_sf, args.tb_len);
pnf.start();
while (running) {
srslte::pnf_metrics_t metrics = pnf.get_metrics();
printf("RTT=%d, #Error=%d, #PDUs=%d, Total TB size=%d, Rate=%.2f Mbit/s\n",
metrics.avg_rtt_us,
metrics.num_timing_errors,
metrics.num_pdus,
metrics.tb_size,
metrics.tb_size * 8 / 1e6);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
pnf.stop();
return 0;
}
Loading…
Cancel
Save