diff --git a/lib/include/srslte/common/network_utils.h b/lib/include/srslte/common/network_utils.h index 88df00507..da6fdbe14 100644 --- a/lib/include/srslte/common/network_utils.h +++ b/lib/include/srslte/common/network_utils.h @@ -86,6 +86,7 @@ public: 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 open_socket(net_utils::addr_family ip, net_utils::socket_type socket_type, net_utils::protocol_type protocol); + int get_socket() { return sockfd; }; protected: sockaddr_in addr = {}; diff --git a/lib/src/common/network_utils.cc b/lib/src/common/network_utils.cc index 52e1eaed1..80e8a73ec 100644 --- a/lib/src/common/network_utils.cc +++ b/lib/src/common/network_utils.cc @@ -121,12 +121,62 @@ int open_socket(net_utils::addr_family ip_type, net_utils::socket_type socket_ty 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) { srslte::logmap::get(LOGSERVICE)->error("Failed to subscribe to SCTP_SHUTDOWN event: %s\n", strerror(errno)); - perror("setsockopt"); + perror("Could not regiester socket to SCTP events\n"); + } + + /* + * 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"); + return -1; + } + + rto_opts.srto_max = 6000; // 6 seconds + + srslte::logmap::get(LOGSERVICE) + ->debug( + "Setting RTO_INFO options on SCTP socket. Association %d, Initial RTO %d, Minimum RTO %d, Maximum RTO %d\n", + 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"); + 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"); + } + + init_opts.sinit_max_attempts = 3; + init_opts.sinit_max_init_timeo = 5000; // 5 seconds + + srslte::logmap::get(LOGSERVICE) + ->debug("Setting SCTP_INITMSG options on SCTP socket. Max attempts %d, Max init attempts timeout %d\n", + 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"); + return -1; } } @@ -368,11 +418,8 @@ class recvfrom_pdu_task final : public rx_multisocket_handler::recv_task public: using callback_t = std::function; explicit recvfrom_pdu_task(srslte::byte_buffer_pool* pool_, srslte::log_ref log_, callback_t func_) : - pool(pool_), - log_h(log_), - func(std::move(func_)) - { - } + pool(pool_), log_h(log_), func(std::move(func_)) + {} bool operator()(int fd) override { @@ -407,11 +454,8 @@ public: using callback_t = std::function< void(srslte::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags)>; explicit sctp_recvmsg_pdu_task(srslte::byte_buffer_pool* pool_, srslte::log_ref log_, callback_t func_) : - pool(pool_), - log_h(log_), - func(std::move(func_)) - { - } + pool(pool_), log_h(log_), func(std::move(func_)) + {} bool operator()(int fd) override { @@ -456,9 +500,7 @@ private: **************************************************************/ rx_multisocket_handler::rx_multisocket_handler(std::string name_, srslte::log_ref log_, int thread_prio) : - thread(name_), - name(std::move(name_)), - log_h(log_) + thread(name_), name(std::move(name_)), log_h(log_) { pool = srslte::byte_buffer_pool::get_instance(); // register control pipe fd diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index 616bdf36d..9aa522c62 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -169,7 +169,6 @@ void s1ap::ue::ho_prep_proc_t::then(const srslte::proc_state_t& result) srslte::proc_outcome_t s1ap::s1_setup_proc_t::init() { procInfo("Starting new MME connection.\n"); - return start_mme_connection(); } @@ -188,7 +187,7 @@ srslte::proc_outcome_t s1ap::s1_setup_proc_t::start_mme_connection() procInfo("Failed to initiate SCTP socket. Attempting reconnection in %d seconds\n", s1ap_ptr->mme_connect_timer.duration() / 1000); srslte::console("Failed to initiate SCTP socket. Attempting reconnection in %d seconds\n", - s1ap_ptr->mme_connect_timer.duration() / 1000); + s1ap_ptr->mme_connect_timer.duration() / 1000); s1ap_ptr->mme_connect_timer.run(); return srslte::proc_outcome_t::error; } @@ -505,6 +504,13 @@ bool s1ap::handle_mme_rx_msg(srslte::unique_byte_buffer_t pdu, if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) { s1ap_log->info("SCTP Association Shutdown. Association: %d\n", sri.sinfo_assoc_id); srslte::console("SCTP Association Shutdown. Association: %d\n", sri.sinfo_assoc_id); + stack->remove_mme_socket(s1ap_socket.get_socket()); + s1ap_socket.reset(); + } else if (notification->sn_header.sn_type == SCTP_PEER_ADDR_CHANGE && + notification->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) { + s1ap_log->info("SCTP peer addres unreachable. Association: %d\n", sri.sinfo_assoc_id); + srslte::console("SCTP peer address unreachable. Association: %d\n", sri.sinfo_assoc_id); + stack->remove_mme_socket(s1ap_socket.get_socket()); s1ap_socket.reset(); } } else if (pdu->N_bytes == 0) {