Merge branch 'next' into agpl_next

This commit is contained in:
Codebot 2022-10-09 21:12:23 +00:00 committed by SRS codebot
commit e1d5f406ef
10 changed files with 242 additions and 110 deletions

View File

@ -83,10 +83,14 @@ public:
std::string get_ip() const { return net_utils::get_ip(addr); }
net_utils::socket_type get_family() const { return net_utils::get_addr_family(sockfd); }
bool open_socket(net_utils::addr_family ip, net_utils::socket_type socket_type, net_utils::protocol_type protocol);
bool bind_addr(const char* bind_addr_str, int port);
bool connect_to(const char* dest_addr_str, int dest_port, sockaddr_in* dest_sockaddr = nullptr);
bool start_listen();
bool open_socket(net_utils::addr_family ip, net_utils::socket_type socket_type, net_utils::protocol_type protocol);
bool reuse_addr();
bool sctp_subscribe_to_events();
bool sctp_set_rto_opts(int rto_max);
bool sctp_set_init_msg_opts(int max_init_attempts, int max_init_timeo);
int get_socket() const { return sockfd; };
protected:

View File

@ -42,6 +42,10 @@ struct s1ap_args_t {
uint32_t ts1_reloc_prep_timeout;
uint32_t ts1_reloc_overall_timeout;
int32_t max_s1_setup_retries;
bool sctp_reuse_addr;
int32_t sctp_rto_max;
int32_t sctp_init_max_attempts;
int32_t sctp_max_init_timeo;
};
// S1AP interface for RRC

View File

@ -118,75 +118,7 @@ int open_socket(net_utils::addr_family ip_type, net_utils::socket_type socket_ty
perror("Could not create socket\n");
return -1;
}
if (protocol == protocol_type::SCTP) {
// Sets the data_io_event to be able to use sendrecv_info
// Subscribes to the SCTP_SHUTDOWN event, to handle graceful shutdown
// Also subscribes to SCTP_PEER_ADDR_CHANGE, to handle ungraceful shutdown of the link.
struct sctp_event_subscribe evnts = {};
evnts.sctp_data_io_event = 1;
evnts.sctp_shutdown_event = 1;
evnts.sctp_address_event = 1;
if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts)) != 0) {
srslog::fetch_basic_logger(LOGSERVICE).error("Failed to subscribe to SCTP_SHUTDOWN event: %s", strerror(errno));
perror("Could not register socket to SCTP events\n");
close(fd);
return -1;
}
/*
* Modify SCTP default parameters for quicker detection of broken links.
* This includes changes to the SCTP_INITMSG parameters (to control the timeout of the connect() syscall)
* And changes to the maximum re-transmission timeout (rto_max), for quicker detection of broken links.
*/
// Set RTO_MAX to quickly detect broken links.
sctp_rtoinfo rto_opts;
socklen_t rto_sz = sizeof(sctp_rtoinfo);
rto_opts.srto_assoc_id = 0;
if (getsockopt(fd, SOL_SCTP, SCTP_RTOINFO, &rto_opts, &rto_sz) < 0) {
printf("Error getting RTO_INFO sockopts\n");
close(fd);
return -1;
}
rto_opts.srto_max = 6000; // 6 seconds
srslog::fetch_basic_logger(LOGSERVICE)
.debug(
"Setting RTO_INFO options on SCTP socket. Association %d, Initial RTO %d, Minimum RTO %d, Maximum RTO %d",
rto_opts.srto_assoc_id,
rto_opts.srto_initial,
rto_opts.srto_min,
rto_opts.srto_max);
if (setsockopt(fd, SOL_SCTP, SCTP_RTOINFO, &rto_opts, rto_sz) < 0) {
perror("Error setting RTO_INFO sockopts\n");
close(fd);
return -1;
}
// Set SCTP INITMSG options to reduce blocking timeout of connect()
sctp_initmsg init_opts;
socklen_t init_sz = sizeof(sctp_initmsg);
if (getsockopt(fd, SOL_SCTP, SCTP_INITMSG, &init_opts, &init_sz) < 0) {
printf("Error getting sockopts\n");
close(fd);
return -1;
}
init_opts.sinit_max_attempts = 3;
init_opts.sinit_max_init_timeo = 5000; // 5 seconds
srslog::fetch_basic_logger(LOGSERVICE)
.debug("Setting SCTP_INITMSG options on SCTP socket. Max attempts %d, Max init attempts timeout %d",
init_opts.sinit_max_attempts,
init_opts.sinit_max_init_timeo);
if (setsockopt(fd, SOL_SCTP, SCTP_INITMSG, &init_opts, init_sz) < 0) {
perror("Error setting SCTP_INITMSG sockopts\n");
close(fd);
return -1;
}
}
srslog::fetch_basic_logger(LOGSERVICE).debug("Opened %s socket=%d", net_utils::protocol_to_string(protocol), fd);
return fd;
}
@ -200,7 +132,12 @@ bool bind_addr(int fd, const sockaddr_in& addr_in)
if (bind(fd, (struct sockaddr*)&addr_in, sizeof(addr_in)) != 0) {
srslog::fetch_basic_logger(LOGSERVICE)
.error("Failed to bind on address %s: %s errno %d", get_ip(addr_in).c_str(), strerror(errno), errno);
.error("Failed to bind on address %s:%d. Socket=%d, strerror=%s, errno=%d",
get_ip(addr_in).c_str(),
get_port(addr_in),
fd,
strerror(errno),
errno);
perror("bind()");
return false;
}
@ -267,6 +204,107 @@ bool start_listen(int fd)
return true;
}
bool reuse_addr(int fd)
{
if (fd < 0) {
srslog::fetch_basic_logger(LOGSERVICE).error("Trying reuse_addr a closed socket. Socket=%d", fd);
return false;
}
int enable = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
srslog::fetch_basic_logger(LOGSERVICE).error("Failed to set SO_REUSEADDR. Socket=%d", fd);
return false;
}
srslog::fetch_basic_logger(LOGSERVICE).debug("Successfully set SO_REUSEADDR. Socket=%d", fd);
return true;
}
bool sctp_subscribe_to_events(int fd)
{
if (fd < 0) {
srslog::fetch_basic_logger(LOGSERVICE).error("Trying subscribe to SCTP events on a closed socket. Socket=%d", fd);
return false;
}
// Sets the data_io_event to be able to use sendrecv_info
// Subscribes to the SCTP_SHUTDOWN event, to handle graceful shutdown
// Also subscribes to SCTP_PEER_ADDR_CHANGE, to handle ungraceful shutdown of the link.
struct sctp_event_subscribe evnts = {};
evnts.sctp_data_io_event = 1;
evnts.sctp_shutdown_event = 1;
evnts.sctp_address_event = 1;
if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts)) != 0) {
srslog::fetch_basic_logger(LOGSERVICE).error("Failed to subscribe to SCTP_SHUTDOWN event: %s", strerror(errno));
perror("Could not register socket to SCTP events\n");
close(fd);
return false;
}
return true;
}
/*
* Modify SCTP default parameters for quicker detection of broken links.
* Changes to the maximum re-transmission timeout (rto_max).
*/
bool sctp_set_rto_opts(int fd, int rto_max)
{
// Set RTO_MAX to quickly detect broken links.
sctp_rtoinfo rto_opts;
socklen_t rto_sz = sizeof(sctp_rtoinfo);
rto_opts.srto_assoc_id = 0;
if (getsockopt(fd, SOL_SCTP, SCTP_RTOINFO, &rto_opts, &rto_sz) < 0) {
printf("Error getting RTO_INFO sockopts\n");
close(fd);
return false;
}
rto_opts.srto_max = rto_max;
srslog::fetch_basic_logger(LOGSERVICE)
.debug("Setting RTO_INFO options on SCTP socket. Association %d, Initial RTO %d, Minimum RTO %d, Maximum RTO %d",
rto_opts.srto_assoc_id,
rto_opts.srto_initial,
rto_opts.srto_min,
rto_opts.srto_max);
if (setsockopt(fd, SOL_SCTP, SCTP_RTOINFO, &rto_opts, rto_sz) < 0) {
perror("Error setting RTO_INFO sockopts\n");
close(fd);
return false;
}
return true;
}
/*
* Modify SCTP default parameters for quicker detection of broken links.
* Changes to the SCTP_INITMSG parameters (to control the timeout of the connect() syscall)
*/
bool sctp_set_init_msg_opts(int fd, int init_max_attempts, int max_init_timeo)
{
// Set SCTP INITMSG options to reduce blocking timeout of connect()
sctp_initmsg init_opts;
socklen_t init_sz = sizeof(sctp_initmsg);
if (getsockopt(fd, SOL_SCTP, SCTP_INITMSG, &init_opts, &init_sz) < 0) {
printf("Error getting sockopts\n");
close(fd);
return false;
}
init_opts.sinit_max_attempts = init_max_attempts;
init_opts.sinit_max_init_timeo = max_init_timeo;
srslog::fetch_basic_logger(LOGSERVICE)
.debug("Setting SCTP_INITMSG options on SCTP socket. Max attempts %d, Max init attempts timeout %d",
init_opts.sinit_max_attempts,
init_opts.sinit_max_init_timeo);
if (setsockopt(fd, SOL_SCTP, SCTP_INITMSG, &init_opts, init_sz) < 0) {
perror("Error setting SCTP_INITMSG sockopts\n");
close(fd);
return false;
}
return true;
}
} // namespace net_utils
/********************************************
@ -306,9 +344,15 @@ bool unique_socket::open_socket(net_utils::addr_family ip_type,
void unique_socket::close()
{
if (sockfd >= 0) {
::close(sockfd);
if (::close(sockfd) == -1) {
srslog::fetch_basic_logger(LOGSERVICE).error("Socket=%d could not be closed.", sockfd);
} else {
srslog::fetch_basic_logger(LOGSERVICE).debug("Socket=%d was closed.", sockfd);
}
sockfd = -1;
addr = {};
} else {
srslog::fetch_basic_logger(LOGSERVICE).debug("Socket=%d could not be closed.", sockfd);
}
}
@ -327,25 +371,25 @@ bool unique_socket::start_listen()
return net_utils::start_listen(sockfd);
}
/***********************************************************************
* SCTP socket
**********************************************************************/
namespace net_utils {
bool sctp_init_socket(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int bind_port)
bool unique_socket::reuse_addr()
{
if (not socket->open_socket(net_utils::addr_family::ipv4, socktype, net_utils::protocol_type::SCTP)) {
return false;
}
if (not socket->bind_addr(bind_addr_str, bind_port)) {
socket->close();
return false;
}
return true;
return net_utils::reuse_addr(sockfd);
}
} // namespace net_utils
bool unique_socket::sctp_subscribe_to_events()
{
return net_utils::sctp_subscribe_to_events(sockfd);
}
bool unique_socket::sctp_set_rto_opts(int rto_max)
{
return net_utils::sctp_set_rto_opts(sockfd, rto_max);
}
bool unique_socket::sctp_set_init_msg_opts(int max_init_attempts, int max_init_timeo)
{
return net_utils::sctp_set_init_msg_opts(sockfd, max_init_attempts, max_init_timeo);
}
/***************************************************************
* Rx Multisocket Handler

View File

@ -428,8 +428,9 @@ static int load_plugin(srsran_rf_plugin_t* rf_plugin)
rf_plugin->dl_handle = dlopen(rf_plugin->plugin_name, RTLD_NOW);
if (rf_plugin->dl_handle == NULL) {
// Not an error, if loading failed due to missing dependencies.
// Mark this plugin as not available and return SUCCESS.
INFO("Failed to load RF plugin %s: %s", rf_plugin->plugin_name, dlerror());
// Flag this plugin as not available and return SUCCESS.
// Note: as this function is called before log-level is configured, use plain printf for any messages < ERROR
printf("Skipping RF plugin %s: %s\n", rf_plugin->plugin_name, dlerror());
rf_plugin->rf_api = NULL;
return SRSRAN_SUCCESS;
}

View File

@ -61,13 +61,19 @@ int test_socket_handler()
const char* server_addr = "127.0.100.1";
using namespace srsran::net_utils;
TESTASSERT(sctp_init_socket(&server_socket, socket_type::seqpacket, server_addr, server_port));
TESTASSERT(server_socket.open_socket(
srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP));
TESTASSERT(server_socket.bind_addr(server_addr, server_port));
TESTASSERT(server_socket.start_listen());
logger.info("Listening from fd=%d", server_socket.fd());
TESTASSERT(sctp_init_socket(&client_socket, socket_type::seqpacket, "127.0.0.1", 0));
TESTASSERT(sctp_init_socket(&client_socket2, socket_type::seqpacket, "127.0.0.2", 0));
TESTASSERT(client_socket.open_socket(
srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP));
TESTASSERT(client_socket.bind_addr("127.0.0.1", 0));
TESTASSERT(client_socket.connect_to(server_addr, server_port));
TESTASSERT(client_socket2.open_socket(
srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP));
TESTASSERT(client_socket2.bind_addr("127.0.0.2", 0));
TESTASSERT(client_socket2.connect_to(server_addr, server_port));
// register server Rx handler
@ -127,12 +133,18 @@ int test_socket_handler()
int test_sctp_bind_error()
{
srsran::unique_socket sock;
TESTASSERT(not srsran::net_utils::sctp_init_socket(
&sock, srsran::net_utils::socket_type::seqpacket, "1.1.1.1", 8000)); // Bogus IP address
// should not be able to bind
TESTASSERT(srsran::net_utils::sctp_init_socket(
&sock, srsran::net_utils::socket_type::seqpacket, "127.0.0.1", 8000)); // Good IP address
// should be able to bind
TESTASSERT(sock.open_socket(srsran::net_utils::addr_family::ipv4,
srsran::net_utils::socket_type::seqpacket,
srsran::net_utils::protocol_type::SCTP));
TESTASSERT(not sock.bind_addr("1.1.1.1", 8000)); // Bogus IP address
// should not be able to bind
srsran::unique_socket sock2;
TESTASSERT(sock2.open_socket(srsran::net_utils::addr_family::ipv4,
srsran::net_utils::socket_type::seqpacket,
srsran::net_utils::protocol_type::SCTP));
TESTASSERT(sock.bind_addr("127.0.0.1", 8000)); // Good IP address
// should be able to bind
return SRSRAN_SUCCESS;
}

View File

@ -39,7 +39,8 @@ public:
enum activity_timeout_type_t {
MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs
UE_INACTIVITY_TIMEOUT, ///< UE inactivity timeout (usually bigger than reestablishment timeout)
MSG5_RX_TIMEOUT, ///< UE timeout for receiving RRCConnectionSetupComplete / RRCReestablishmentComplete
MSG5_RX_TIMEOUT_T300, ///< UE timeout for receiving RRCConnectionSetupComplete
MSG5_RX_TIMEOUT_T301, ///< UE timeout for receiving RRCReestablishmentComplete
nulltype
};

View File

@ -267,7 +267,12 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("expert.ts1_reloc_prep_timeout", bpo::value<uint32_t>(&args->stack.s1ap.ts1_reloc_prep_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocPrep Expiry Timeout value in milliseconds.")
("expert.ts1_reloc_overall_timeout", bpo::value<uint32_t>(&args->stack.s1ap.ts1_reloc_overall_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocOverall Expiry Timeout value in milliseconds.")
("expert.rlf_min_ul_snr_estim", bpo::value<int>(&args->stack.mac.rlf_min_ul_snr_estim)->default_value(-2), "SNR threshold in dB below which the eNB is notified with rlf ko.")
("expert.sctp_reuse_addr", bpo::value<bool>(&args->stack.s1ap.sctp_reuse_addr)->default_value(false), "Use SO_REUSE_ADDR on S1-C interface.")
("expert.max_s1_setup_retries", bpo::value<int32_t>(&args->stack.s1ap.max_s1_setup_retries)->default_value(-1), "Max S1 setup retries")
("expert.sctp_rto_max", bpo::value<int32_t>(&args->stack.s1ap.sctp_rto_max)->default_value(6000), "SCTP maximum RTO.")
("expert.sctp_init_max_attempts", bpo::value<int32_t>(&args->stack.s1ap.sctp_init_max_attempts)->default_value(3), "Maximum SCTP init attempts.")
("expert.sctp_max_init_timeo)", bpo::value<int32_t>(&args->stack.s1ap.sctp_max_init_timeo)->default_value(5000), "Maximum SCTP init timeout.")
("expert.rx_gain_offset", bpo::value<float>(&args->phy.rx_gain_offset)->default_value(62), "RX Gain offset to add to rx_gain to calibrate RSRP readings")
("expert.mac_prach_bi", bpo::value<uint32_t>(&args->stack.mac.prach_bi)->default_value(0), "Backoff Indicator to reduce contention in the PRACH channel")

View File

@ -221,7 +221,8 @@ void rrc::ue::activity_timer_expired(const activity_timeout_type_t type)
con_release_result = procedure_result_code::activity_timeout;
break;
case MSG3_RX_TIMEOUT:
case MSG5_RX_TIMEOUT:
case MSG5_RX_TIMEOUT_T300:
case MSG5_RX_TIMEOUT_T301:
// MSG3 timeout, no need to notify S1AP, just remove UE
parent->rem_user_thread(rnti);
con_release_result = procedure_result_code::msg3_timeout;
@ -301,7 +302,10 @@ void rrc::ue::set_activity_timeout(activity_timeout_type_t type)
case UE_INACTIVITY_TIMEOUT:
deadline_ms = parent->cfg.inactivity_timeout_ms;
break;
case MSG5_RX_TIMEOUT:
case MSG5_RX_TIMEOUT_T300:
deadline_ms = get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t300.to_number();
break;
case MSG5_RX_TIMEOUT_T301:
deadline_ms = get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t301.to_number();
break;
default:
@ -350,6 +354,7 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_setup_complete:
save_ul_message(std::move(original_pdu));
handle_rrc_con_setup_complete(&ul_dcch_msg.msg.c1().rrc_conn_setup_complete(), std::move(pdu));
set_activity_timeout(UE_INACTIVITY_TIMEOUT);
set_activity();
break;
case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_reest_complete:
@ -429,7 +434,7 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
std::string rrc::ue::to_string(const activity_timeout_type_t& type)
{
constexpr static const char* options[] = {"Msg3 reception", "UE inactivity", "UE reestablishment"};
constexpr static const char* options[] = {"Msg3 reception", "UE inactivity", "UE establishment", "UE reestablishment"};
return srsran::enum_to_text(options, (uint32_t)activity_timeout_type_t::nulltype, (uint32_t)type);
}
@ -483,7 +488,7 @@ void rrc::ue::handle_rrc_con_req(rrc_conn_request_s* msg)
send_connection_setup();
state = RRC_STATE_WAIT_FOR_CON_SETUP_COMPLETE;
set_activity_timeout(UE_INACTIVITY_TIMEOUT);
set_activity_timeout(MSG5_RX_TIMEOUT_T300);
}
void rrc::ue::send_connection_setup()
@ -739,7 +744,7 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
parent->rem_user_thread(old_rnti);
state = RRC_STATE_WAIT_FOR_CON_REEST_COMPLETE;
set_activity_timeout(MSG5_RX_TIMEOUT);
set_activity_timeout(MSG5_RX_TIMEOUT_T301);
}
void rrc::ue::send_connection_reest(uint8_t ncc)

View File

@ -500,9 +500,39 @@ bool s1ap::connect_mme()
using namespace srsran::net_utils;
logger.info("Connecting to MME %s:%d", args.mme_addr.c_str(), int(MME_PORT));
// Init SCTP socket and bind it
if (not srsran::net_utils::sctp_init_socket(
&mme_socket, socket_type::seqpacket, args.s1c_bind_addr.c_str(), args.s1c_bind_port)) {
// Open SCTP socket
if (not mme_socket.open_socket(
srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP)) {
return false;
}
// Set SO_REUSE_ADDR if necessary
if (args.sctp_reuse_addr) {
if (not mme_socket.reuse_addr()) {
mme_socket.close();
return false;
}
}
// Subscribe to shutdown events
if (not mme_socket.sctp_subscribe_to_events()) {
mme_socket.close();
return false;
}
// Set SRTO_MAX
if (not mme_socket.sctp_set_rto_opts(args.sctp_rto_max)) {
return false;
}
// Set SCTP init options
if (not mme_socket.sctp_set_init_msg_opts(args.sctp_init_max_attempts, args.sctp_max_init_timeo)) {
return false;
}
// Bind socket
if (not mme_socket.bind_addr(args.s1c_bind_addr.c_str(), args.s1c_bind_port)) {
mme_socket.close();
return false;
}
logger.info("SCTP socket opened. fd=%d", mme_socket.fd());
@ -1112,6 +1142,10 @@ bool s1ap::handle_s1setupfailure(const asn1::s1ap::s1_setup_fail_s& msg)
return false;
}
s1_setup_proc_t::s1setupresult res;
res.success = false;
s1setup_proc.trigger(res);
std::string cause = get_cause(msg->cause.value);
logger.error("S1 Setup Failure. Cause: %s", cause.c_str());
srsran::console("S1 Setup Failure. Cause: %s\n", cause.c_str());

View File

@ -642,12 +642,34 @@ bool ngap::connect_amf()
using namespace srsran::net_utils;
logger.info("Connecting to AMF %s:%d", args.amf_addr.c_str(), int(AMF_PORT));
// Init SCTP socket and bind it
if (not sctp_init_socket(&amf_socket, socket_type::seqpacket, args.ngc_bind_addr.c_str(), 0)) {
// Open SCTP socket
if (not amf_socket.open_socket(
srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP)) {
return false;
}
logger.info("SCTP socket opened. fd=%d", amf_socket.fd());
if (not amf_socket.sctp_subscribe_to_events()) {
amf_socket.close();
return false;
}
if (not amf_socket.sctp_set_rto_opts(5000)) {
amf_socket.close();
return false;
}
if (not amf_socket.sctp_set_init_msg_opts(3, 6000)) {
amf_socket.close();
return false;
}
// Bind socket
if (not amf_socket.bind_addr(args.ngc_bind_addr.c_str(), 0)) {
amf_socket.close();
return false;
}
// Connect to the AMF address
if (not amf_socket.connect_to(args.amf_addr.c_str(), AMF_PORT, &amf_addr)) {
return false;