diff --git a/lib/include/srsran/common/network_utils.h b/lib/include/srsran/common/network_utils.h index 8e3de4ac1..b167d5d2e 100644 --- a/lib/include/srsran/common/network_utils.h +++ b/lib/include/srsran/common/network_utils.h @@ -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: diff --git a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h index b6b2a0741..1ffd82026 100644 --- a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h +++ b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h @@ -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; diff --git a/lib/src/common/network_utils.cc b/lib/src/common/network_utils.cc index a2d9ed91b..8e286413a 100644 --- a/lib/src/common/network_utils.cc +++ b/lib/src/common/network_utils.cc @@ -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 - **********************************************************************/ +bool unique_socket::reuse_addr() +{ + 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 sctp_init_socket(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int bind_port) +bool unique_socket::sctp_set_rto_opts() { - 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::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 diff --git a/lib/test/common/network_utils_test.cc b/lib/test/common/network_utils_test.cc index 31fbe2b4c..b193b1d3f 100644 --- a/lib/test/common/network_utils_test.cc +++ b/lib/test/common/network_utils_test.cc @@ -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; } diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 71721b4c0..4bd8ee807 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -79,6 +79,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("enb.gtp_advertise_addr", bpo::value(&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(&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(&args->stack.s1ap.s1c_bind_port)->default_value(0), "Source port for S1AP connection (0 means any)") + ("enb.s1c_reuse_addr", bpo::value(&args->stack.s1ap.s1c_reuse_addr)->default_value(false), "Use SO_REUSE_ADDR on S1-C interface.") ("enb.n_prb", bpo::value(&args->enb.n_prb)->default_value(25), "Number of PRB") ("enb.nof_ports", bpo::value(&args->enb.nof_ports)->default_value(1), "Number of ports") ("enb.tm", bpo::value(&args->enb.transmission_mode)->default_value(1), "Transmission mode (1-8)") diff --git a/srsenb/src/stack/s1ap/s1ap.cc b/srsenb/src/stack/s1ap/s1ap.cc index 6fb124728..59ab32830 100644 --- a/srsenb/src/stack/s1ap/s1ap.cc +++ b/srsenb/src/stack/s1ap/s1ap.cc @@ -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()); diff --git a/srsgnb/src/stack/ngap/ngap.cc b/srsgnb/src/stack/ngap/ngap.cc index 16a44188b..878b0a31f 100644 --- a/srsgnb/src/stack/ngap/ngap.cc +++ b/srsgnb/src/stack/ngap/ngap.cc @@ -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;