mirror of https://github.com/pvnis/srsRAN_4G.git
ttcn3: make SS single threaded and use SCTP for test ports
- remove all threading from SS - use epoll to handle test port connections, signals, and timers - convert testport interface to SCTPmaster
parent
6d88b640b7
commit
b88e877b4c
@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \brief Common helper function for epoll
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSLTE_EPOLL_HELPER_H
|
||||||
|
#define SRSLTE_EPOLL_HELPER_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/signalfd.h>
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
///< A virtual interface to handle epoll events (used by timer and port handler)
|
||||||
|
class epoll_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int handle_event(int fd, epoll_event e, int epoll_fd) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
///< Callback function called when timer expires
|
||||||
|
using epoll_timer_callback = std::function<void(uint64_t res)>;
|
||||||
|
|
||||||
|
///< Epoll timer handler
|
||||||
|
class epoll_timer_handler : public epoll_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
epoll_timer_handler(int fd_, epoll_timer_callback callback_) : timer_fd(fd_), callback(callback_){};
|
||||||
|
int handle_event(int fd, epoll_event e, int epoll_fd)
|
||||||
|
{
|
||||||
|
uint64_t res;
|
||||||
|
int ret = read(fd, &res, sizeof(res));
|
||||||
|
callback(res);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_timer_fd() { return timer_fd; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
int timer_fd = -1;
|
||||||
|
epoll_timer_callback callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
///< Basic epoll signal handler
|
||||||
|
class epoll_signal_handler : public epoll_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
epoll_signal_handler(bool* running_) : running(running_) {}
|
||||||
|
|
||||||
|
int handle_event(int fd, epoll_event e, int epoll_fd)
|
||||||
|
{
|
||||||
|
struct signalfd_siginfo info;
|
||||||
|
if (read(fd, &info, sizeof(info)) != sizeof(info)) {
|
||||||
|
fprintf(stderr, "failed to read signal fd buffer\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
switch (info.ssi_signo) {
|
||||||
|
case SIGTERM:
|
||||||
|
case SIGINT:
|
||||||
|
case SIGHUP:
|
||||||
|
case SIGQUIT:
|
||||||
|
*running = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "got signal %d\n", info.ssi_signo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool* running = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
///< Create periodic epoll timer every 1ms
|
||||||
|
int create_tti_timer()
|
||||||
|
{
|
||||||
|
int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
|
||||||
|
if (timer_fd == -1) {
|
||||||
|
printf("timerfd_create() failed: errno=%d\n", errno);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int msec = 1; // our 1ms TTI timer
|
||||||
|
struct itimerspec ts = {};
|
||||||
|
ts.it_value.tv_sec = msec / 1000;
|
||||||
|
ts.it_value.tv_nsec = (msec % 1000) * 1000000;
|
||||||
|
ts.it_interval.tv_sec = msec / 1000;
|
||||||
|
ts.it_interval.tv_nsec = (msec % 1000) * 1000000;
|
||||||
|
|
||||||
|
if (timerfd_settime(timer_fd, 0, &ts, NULL) < 0) {
|
||||||
|
printf("timerfd_settime() failed: errno=%d\n", errno);
|
||||||
|
close(timer_fd);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
return timer_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
///< Create signalfd for handling signals
|
||||||
|
int add_signalfd()
|
||||||
|
{
|
||||||
|
// block all signals. we take signals synchronously via signalfd
|
||||||
|
sigset_t all;
|
||||||
|
sigfillset(&all);
|
||||||
|
sigprocmask(SIG_SETMASK, &all, NULL);
|
||||||
|
|
||||||
|
// add signals we accept synchronously via signalfd
|
||||||
|
std::vector<int> sigs = {SIGIO, SIGHUP, SIGTERM, SIGINT, SIGQUIT, SIGALRM};
|
||||||
|
|
||||||
|
sigset_t sw;
|
||||||
|
sigemptyset(&sw);
|
||||||
|
for (auto& sig : sigs) {
|
||||||
|
sigaddset(&sw, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the signalfd for receiving signals
|
||||||
|
int sig_fd = signalfd(-1, &sw, 0);
|
||||||
|
if (sig_fd == -1) {
|
||||||
|
fprintf(stderr, "signalfd: %s\n", strerror(errno));
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
return sig_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
///< Add fd to epoll fd
|
||||||
|
int add_epoll(int fd, int epoll_fd)
|
||||||
|
{
|
||||||
|
struct epoll_event ev = {};
|
||||||
|
ev.data.fd = fd;
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
|
||||||
|
fprintf(stderr, "epoll_ctl failed for fd=%d\n", fd);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
///< Remove fd from epoll
|
||||||
|
int del_epoll(int fd, int epoll_fd)
|
||||||
|
{
|
||||||
|
struct epoll_event ev = {};
|
||||||
|
ev.data.fd = fd;
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev) == -1) {
|
||||||
|
fprintf(stderr, "epoll_ctl failed for fd=%d\n", fd);
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SRSLTE_EPOLL_HELPER_H
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \brief Common types for TTCN3 test systems
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSUE_TTCN3_COMMON_H
|
||||||
|
#define SRSUE_TTCN3_COMMON_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
const static uint32_t RX_BUF_SIZE = 1024 * 1024;
|
||||||
|
typedef std::array<uint8_t, RX_BUF_SIZE> byte_array_t;
|
||||||
|
typedef std::unique_ptr<byte_array_t> unique_byte_array_t;
|
||||||
|
|
||||||
|
#endif // SRSUE_TTCN3_COMMON_H
|
@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \brief This class provides a common function for all TTCN3
|
||||||
|
* ports for SCTP socket creation, notification handling, etc.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSUE_TTCN3_PORT_HANDLER_H
|
||||||
|
#define SRSUE_TTCN3_PORT_HANDLER_H
|
||||||
|
|
||||||
|
#include "srslte/common/epoll_helper.h"
|
||||||
|
#include "srslte/common/log.h"
|
||||||
|
#include "ttcn3_common.h"
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <netinet/sctp.h>
|
||||||
|
|
||||||
|
class ttcn3_port_handler : public epoll_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ttcn3_port_handler() : rx_buf(unique_byte_array_t(new byte_array_t)) {}
|
||||||
|
virtual ~ttcn3_port_handler() {}
|
||||||
|
|
||||||
|
virtual int handle_message(const unique_byte_array_t& rx_buf, const uint32_t n) = 0;
|
||||||
|
|
||||||
|
int handle_event(int fd, epoll_event e, int epoll_fd)
|
||||||
|
{
|
||||||
|
if (e.events & EPOLLIN) {
|
||||||
|
struct sctp_sndrcvinfo sri = {};
|
||||||
|
socklen_t fromlen = sizeof(client_addr);
|
||||||
|
int msg_flags = 0;
|
||||||
|
int rd_sz =
|
||||||
|
sctp_recvmsg(fd, rx_buf->begin(), RX_BUF_SIZE, (struct sockaddr*)&client_addr, &fromlen, &sri, &msg_flags);
|
||||||
|
if (rd_sz == -1 && errno != EAGAIN) {
|
||||||
|
log->error("Error reading from SCTP socket: %s", strerror(errno));
|
||||||
|
} else if (rd_sz == -1 && errno == EAGAIN) {
|
||||||
|
log->debug("Socket timeout reached");
|
||||||
|
} else {
|
||||||
|
if (msg_flags & MSG_NOTIFICATION) {
|
||||||
|
// Received notification
|
||||||
|
handle_notification(rx_buf->begin(), rd_sz);
|
||||||
|
} else {
|
||||||
|
// Received data
|
||||||
|
rx_buf->at(rd_sz) = '\0'; // Terminate buffer
|
||||||
|
handle_message(rx_buf, rd_sz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_notification(const uint8_t* payload, const uint32_t len)
|
||||||
|
{
|
||||||
|
union sctp_notification* notif = (union sctp_notification*)payload;
|
||||||
|
uint32_t notif_header_size = sizeof(((union sctp_notification*)NULL)->sn_header);
|
||||||
|
if (notif_header_size > len) {
|
||||||
|
printf("Error: Notification msg size is smaller than notification header size!\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (notif->sn_header.sn_type) {
|
||||||
|
case SCTP_ASSOC_CHANGE: {
|
||||||
|
if (sizeof(struct sctp_assoc_change) > len) {
|
||||||
|
printf("Error notification msg size is smaller than struct sctp_assoc_change size\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* state = NULL;
|
||||||
|
struct sctp_assoc_change* n = ¬if->sn_assoc_change;
|
||||||
|
|
||||||
|
switch (n->sac_state) {
|
||||||
|
case SCTP_COMM_UP:
|
||||||
|
state = "COMM UP";
|
||||||
|
break;
|
||||||
|
case SCTP_COMM_LOST:
|
||||||
|
state = "COMM_LOST";
|
||||||
|
break;
|
||||||
|
case SCTP_RESTART:
|
||||||
|
state = "RESTART";
|
||||||
|
break;
|
||||||
|
case SCTP_SHUTDOWN_COMP:
|
||||||
|
state = "SHUTDOWN_COMP";
|
||||||
|
break;
|
||||||
|
case SCTP_CANT_STR_ASSOC:
|
||||||
|
state = "CAN'T START ASSOC";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
log->debug(
|
||||||
|
"SCTP_ASSOC_CHANGE notif: state: %s, error code: %d, out streams: %d, in streams: %d, assoc id: %d\n",
|
||||||
|
state,
|
||||||
|
n->sac_error,
|
||||||
|
n->sac_outbound_streams,
|
||||||
|
n->sac_inbound_streams,
|
||||||
|
n->sac_assoc_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SCTP_SHUTDOWN_EVENT: {
|
||||||
|
if (sizeof(struct sctp_shutdown_event) > len) {
|
||||||
|
printf("Error notification msg size is smaller than struct sctp_assoc_change size\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
struct sctp_shutdown_event* n = ¬if->sn_shutdown_event;
|
||||||
|
log->debug("SCTP_SHUTDOWN_EVENT notif: assoc id: %d\n", n->sse_assoc_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
log->warning("Unhandled notification type %d\n", notif->sn_header.sn_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
///< Send buffer to tester
|
||||||
|
void send(const uint8_t* buffer, const uint32_t len)
|
||||||
|
{
|
||||||
|
if (sendto(sock_fd, buffer, len, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)) == -1) {
|
||||||
|
log->error("Error sending message to tester.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///< Set socket to non-blocking-mode
|
||||||
|
int set_non_blocking(uint32_t fd)
|
||||||
|
{
|
||||||
|
int flags = fcntl(fd, F_GETFL, 0);
|
||||||
|
if (flags == -1) {
|
||||||
|
perror("fcntl");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
int s = fcntl(fd, F_SETFL, flags);
|
||||||
|
if (s == -1) {
|
||||||
|
perror("fcntl");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
///< Create, bind and listen on SCTP socket
|
||||||
|
int port_listen()
|
||||||
|
{
|
||||||
|
int ret = SRSLTE_ERROR;
|
||||||
|
|
||||||
|
sock_fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
|
||||||
|
if (sock_fd == -1) {
|
||||||
|
log->console("Could not create SCTP socket\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the data_io_event to be able to use sendrecv_info
|
||||||
|
// Subscribes to the SCTP_SHUTDOWN event, to handle graceful shutdown
|
||||||
|
struct sctp_event_subscribe events = {};
|
||||||
|
events.sctp_data_io_event = 1;
|
||||||
|
events.sctp_shutdown_event = 1;
|
||||||
|
events.sctp_association_event = 1;
|
||||||
|
if (setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events))) {
|
||||||
|
close(sock_fd);
|
||||||
|
log->console("Subscribing to sctp_data_io_events failed\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Port bind
|
||||||
|
struct sockaddr_in bind_addr = {};
|
||||||
|
bind_addr.sin_family = AF_INET;
|
||||||
|
inet_pton(AF_INET, net_ip.c_str(), &(bind_addr.sin_addr));
|
||||||
|
bind_addr.sin_port = htons(net_port);
|
||||||
|
|
||||||
|
int one = 1;
|
||||||
|
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||||
|
ret = bind(sock_fd, (struct sockaddr*)&bind_addr, sizeof(bind_addr));
|
||||||
|
if (ret != 0) {
|
||||||
|
close(sock_fd);
|
||||||
|
log->error("Error binding SCTP socket\n");
|
||||||
|
log->console("Error binding SCTP socket\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for connections
|
||||||
|
ret = listen(sock_fd, SOMAXCONN);
|
||||||
|
if (ret != SRSLTE_SUCCESS) {
|
||||||
|
close(sock_fd);
|
||||||
|
log->error("Error in SCTP socket listen\n");
|
||||||
|
log->console("Error in SCTP socket listen\n");
|
||||||
|
return SRSLTE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_non_blocking(sock_fd);
|
||||||
|
return sock_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initialized = false;
|
||||||
|
std::string net_ip = "0.0.0.0";
|
||||||
|
uint32_t net_port = 0;
|
||||||
|
int sock_fd = -1;
|
||||||
|
struct sockaddr client_addr = {};
|
||||||
|
srslte::log* log = nullptr;
|
||||||
|
unique_byte_array_t rx_buf; ///< Receive buffer for this port
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SRSUE_TTCN3_PORT_HANDLER_H
|
Loading…
Reference in New Issue