netutils,s1ap: split setting the SCTP options into different functions.

Added option for SO_REUSE_ADDR. Removed sctp_init_socket function.
This commit is contained in:
Pedro Alvarez 2022-04-05 19:00:29 +01:00
parent 142bfd6ea8
commit 08d03ee6e2
7 changed files with 176 additions and 97 deletions

View File

@ -74,10 +74,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();
bool sctp_set_init_msg_opts();
int get_socket() const { return sockfd; };
protected:

View File

@ -29,6 +29,7 @@ struct s1ap_args_t {
std::string gtp_advertise_addr;
std::string s1c_bind_addr;
uint16_t s1c_bind_port;
bool s1c_reuse_addr;
std::string enb_name;
uint32_t ts1_reloc_prep_timeout;
uint32_t ts1_reloc_overall_timeout;

View File

@ -112,72 +112,6 @@ int open_socket(net_utils::addr_family ip_type, net_utils::socket_type socket_ty
srslog::fetch_basic_logger(LOGSERVICE).debug("Opened %s socket=%d", net_utils::protocol_to_string(protocol), fd);
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;
}
}
return fd;
@ -264,6 +198,104 @@ 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;
}
bool sctp_set_rto_opts(int fd)
{
/*
* 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 false;
}
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 false;
}
return true;
}
bool sctp_set_init_msg_opts(int fd)
{
// 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 = 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 false;
}
return true;
}
} // namespace net_utils
/********************************************
@ -330,25 +362,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()
{
return net_utils::sctp_set_rto_opts(sockfd);
}
bool unique_socket::sctp_set_init_msg_opts()
{
return net_utils::sctp_set_init_msg_opts(sockfd);
}
/***************************************************************
* Rx Multisocket Handler

View File

@ -52,13 +52,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
@ -118,12 +124,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(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

@ -79,6 +79,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("enb.gtp_advertise_addr", bpo::value<string>(&args->stack.s1ap.gtp_advertise_addr)->default_value(""), "IP address of eNB to advertise for DL GTP-U Traffic")
("enb.s1c_bind_addr", bpo::value<string>(&args->stack.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection")
("enb.s1c_bind_port", bpo::value<uint16_t>(&args->stack.s1ap.s1c_bind_port)->default_value(0), "Source port for S1AP connection (0 means any)")
("enb.s1c_reuse_addr", bpo::value<bool>(&args->stack.s1ap.s1c_reuse_addr)->default_value(false), "Use SO_REUSE_ADDR on S1-C interface.")
("enb.n_prb", bpo::value<uint32_t>(&args->enb.n_prb)->default_value(25), "Number of PRB")
("enb.nof_ports", bpo::value<uint32_t>(&args->enb.nof_ports)->default_value(1), "Number of ports")
("enb.tm", bpo::value<uint32_t>(&args->enb.transmission_mode)->default_value(1), "Transmission mode (1-8)")

View File

@ -491,9 +491,27 @@ 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.s1c_reuse_addr) {
if (not mme_socket.reuse_addr()) {
mme_socket.close();
return false;
}
}
mme_socket.sctp_subscribe_to_events();
mme_socket.sctp_set_rto_opts();
mme_socket.sctp_set_init_msg_opts();
// 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());

View File

@ -633,12 +633,23 @@ 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());
amf_socket.sctp_subscribe_to_events();
amf_socket.sctp_set_rto_opts();
amf_socket.sctp_set_init_msg_opts();
// 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;