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

Added option for SO_REUSE_ADDR. Removed sctp_init_socket function.
master
Pedro Alvarez 3 years ago
parent 142bfd6ea8
commit 08d03ee6e2

@ -74,10 +74,14 @@ public:
std::string get_ip() const { return net_utils::get_ip(addr); } 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); } 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 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 connect_to(const char* dest_addr_str, int dest_port, sockaddr_in* dest_sockaddr = nullptr);
bool start_listen(); 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; }; int get_socket() const { return sockfd; };
protected: protected:

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

@ -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); srslog::fetch_basic_logger(LOGSERVICE).debug("Opened %s socket=%d", net_utils::protocol_to_string(protocol), fd);
if (protocol == protocol_type::SCTP) { 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; return fd;
@ -264,6 +198,104 @@ bool start_listen(int fd)
return true; 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 } // namespace net_utils
/******************************************** /********************************************
@ -330,25 +362,25 @@ bool unique_socket::start_listen()
return net_utils::start_listen(sockfd); return net_utils::start_listen(sockfd);
} }
/*********************************************************************** bool unique_socket::reuse_addr()
* 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)
{ {
if (not socket->open_socket(net_utils::addr_family::ipv4, socktype, net_utils::protocol_type::SCTP)) { return net_utils::reuse_addr(sockfd);
return false;
} }
if (not socket->bind_addr(bind_addr_str, bind_port)) {
socket->close(); bool unique_socket::sctp_subscribe_to_events()
return false; {
return net_utils::sctp_subscribe_to_events(sockfd);
} }
return true;
bool unique_socket::sctp_set_rto_opts()
{
return net_utils::sctp_set_rto_opts(sockfd);
} }
} // namespace net_utils bool unique_socket::sctp_set_init_msg_opts()
{
return net_utils::sctp_set_init_msg_opts(sockfd);
}
/*************************************************************** /***************************************************************
* Rx Multisocket Handler * Rx Multisocket Handler

@ -52,13 +52,19 @@ int test_socket_handler()
const char* server_addr = "127.0.100.1"; const char* server_addr = "127.0.100.1";
using namespace srsran::net_utils; 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()); TESTASSERT(server_socket.start_listen());
logger.info("Listening from fd=%d", server_socket.fd()); logger.info("Listening from fd=%d", server_socket.fd());
TESTASSERT(sctp_init_socket(&client_socket, socket_type::seqpacket, "127.0.0.1", 0)); TESTASSERT(client_socket.open_socket(
TESTASSERT(sctp_init_socket(&client_socket2, socket_type::seqpacket, "127.0.0.2", 0)); 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_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)); TESTASSERT(client_socket2.connect_to(server_addr, server_port));
// register server Rx handler // register server Rx handler
@ -118,11 +124,17 @@ int test_socket_handler()
int test_sctp_bind_error() int test_sctp_bind_error()
{ {
srsran::unique_socket sock; srsran::unique_socket sock;
TESTASSERT(not srsran::net_utils::sctp_init_socket( TESTASSERT(sock.open_socket(srsran::net_utils::addr_family::ipv4,
&sock, srsran::net_utils::socket_type::seqpacket, "1.1.1.1", 8000)); // Bogus IP address 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 // 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 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 // should be able to bind
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }

@ -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.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_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_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.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.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)") ("enb.tm", bpo::value<uint32_t>(&args->enb.transmission_mode)->default_value(1), "Transmission mode (1-8)")

@ -491,9 +491,27 @@ bool s1ap::connect_mme()
using namespace srsran::net_utils; using namespace srsran::net_utils;
logger.info("Connecting to MME %s:%d", args.mme_addr.c_str(), int(MME_PORT)); logger.info("Connecting to MME %s:%d", args.mme_addr.c_str(), int(MME_PORT));
// Init SCTP socket and bind it // Open SCTP socket
if (not srsran::net_utils::sctp_init_socket( if (not mme_socket.open_socket(
&mme_socket, socket_type::seqpacket, args.s1c_bind_addr.c_str(), args.s1c_bind_port)) { 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; return false;
} }
logger.info("SCTP socket opened. fd=%d", mme_socket.fd()); logger.info("SCTP socket opened. fd=%d", mme_socket.fd());

@ -633,12 +633,23 @@ bool ngap::connect_amf()
using namespace srsran::net_utils; using namespace srsran::net_utils;
logger.info("Connecting to AMF %s:%d", args.amf_addr.c_str(), int(AMF_PORT)); logger.info("Connecting to AMF %s:%d", args.amf_addr.c_str(), int(AMF_PORT));
// Init SCTP socket and bind it // Open SCTP socket
if (not sctp_init_socket(&amf_socket, socket_type::seqpacket, args.ngc_bind_addr.c_str(), 0)) { if (not amf_socket.open_socket(
srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP)) {
return false; return false;
} }
logger.info("SCTP socket opened. fd=%d", amf_socket.fd()); 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 // Connect to the AMF address
if (not amf_socket.connect_to(args.amf_addr.c_str(), AMF_PORT, &amf_addr)) { if (not amf_socket.connect_to(args.amf_addr.c_str(), AMF_PORT, &amf_addr)) {
return false; return false;

Loading…
Cancel
Save