From c117b563c94f045133c070998c6449ef0ec29265 Mon Sep 17 00:00:00 2001 From: David Rupprecht Date: Tue, 4 May 2021 19:08:46 +0200 Subject: [PATCH] Initial commit for ngap in enb --- lib/include/srsran/asn1/ngap_utils.h | 31 ++ .../srsran/interfaces/gnb_interfaces.h | 12 - .../srsran/interfaces/gnb_ngap_interfaces.h | 60 +++ .../srsran/interfaces/gnb_rrc_nr_interfaces.h | 24 ++ srsenb/hdr/stack/rrc/rrc_nr.h | 2 + srsenb/hdr/stack/upper/ngap.h | 134 ++++++ srsenb/src/stack/upper/CMakeLists.txt | 2 +- srsenb/src/stack/upper/ngap.cc | 405 ++++++++++++++++++ 8 files changed, 657 insertions(+), 13 deletions(-) create mode 100644 lib/include/srsran/asn1/ngap_utils.h create mode 100644 lib/include/srsran/interfaces/gnb_ngap_interfaces.h create mode 100644 lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h create mode 100644 srsenb/hdr/stack/upper/ngap.h create mode 100644 srsenb/src/stack/upper/ngap.cc diff --git a/lib/include/srsran/asn1/ngap_utils.h b/lib/include/srsran/asn1/ngap_utils.h new file mode 100644 index 000000000..59d16c487 --- /dev/null +++ b/lib/include/srsran/asn1/ngap_utils.h @@ -0,0 +1,31 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_NGAP_UTILS_H +#define SRSRAN_NGAP_UTILS_H + +#include "asn1_utils.h" +#include "ngap.h" +/************************ + * Forward declarations + ***********************/ + +namespace asn1 { +namespace ngap_nr { +struct rrcestablishment_cause_opts; +struct cause_radio_network_opts; +using rrcestablishment_cause_e = enumerated; +using cause_radio_network_e = enumerated; +} // namespace ngap +} // namespace asn1 + +#endif // SRSRAN_NGAP_UTILS_H \ No newline at end of file diff --git a/lib/include/srsran/interfaces/gnb_interfaces.h b/lib/include/srsran/interfaces/gnb_interfaces.h index 31681a9be..35f19d651 100644 --- a/lib/include/srsran/interfaces/gnb_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_interfaces.h @@ -159,18 +159,6 @@ class rrc_interface_pdcp_nr public: virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; }; -class rrc_interface_ngap_nr -{ -public: -}; - -/***************************** - * NGAP INTERFACES - ****************************/ -class ngap_interface_rrc_nr -{ -public: -}; class phy_interface_stack_nr { diff --git a/lib/include/srsran/interfaces/gnb_ngap_interfaces.h b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h new file mode 100644 index 000000000..ff39c7ffe --- /dev/null +++ b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h @@ -0,0 +1,60 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_GNB_NGAP_INTERFACES_H +#define SRSRAN_GNB_NGAP_INTERFACES_H + +#include "srsran/asn1/ngap_utils.h" + +namespace srsenb { + +struct ngap_args_t { + uint32_t gnb_id; // 20-bit id (lsb bits) + uint8_t cell_id; // 8-bit cell id + uint16_t tac; // 16-bit tac + uint16_t mcc; // BCD-coded with 0xF filler + uint16_t mnc; // BCD-coded with 0xF filler + std::string amf_addr; + std::string gtp_bind_addr; + std::string gtp_advertise_addr; + std::string ngc_bind_addr; + std::string gnb_name; +}; + +// NGAP interface for RRC +class ngap_interface_rrc_nr +{ +public: + virtual void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap_nr::rrcestablishment_cause_e cause, + srsran::unique_byte_buffer_t pdu) = 0; + virtual void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap_nr::rrcestablishment_cause_e cause, + srsran::unique_byte_buffer_t pdu, + uint32_t m_tmsi, + uint8_t mmec) = 0; + + virtual void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu) = 0; + virtual bool user_exists(uint16_t rnti) = 0; + virtual void user_mod(uint16_t old_rnti, uint16_t new_rnti) = 0; + virtual bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) = 0; + virtual bool is_amf_connected() = 0; + + /// TS 36.413, 8.3.1 - Initial Context Setup + virtual void ue_ctxt_setup_complete(uint16_t rnti) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_GNB_NGAP_INTERFACES_H \ No newline at end of file diff --git a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h new file mode 100644 index 000000000..af7ec46a3 --- /dev/null +++ b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h @@ -0,0 +1,24 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#ifndef SRSRAN_GNB_RRC_NR_INTERFACES_H +#define SRSRAN_GNB_RRC_NR_INTERFACES_H + +namespace srsenb { + +class rrc_interface_ngap_nr +{ +public: +}; + +} // namespace srsenb + +#endif // SRSRAN_GNB_RRC_NR_INTERFACES_H \ No newline at end of file diff --git a/srsenb/hdr/stack/rrc/rrc_nr.h b/srsenb/hdr/stack/rrc/rrc_nr.h index 0e2f3b944..b8fbf1622 100644 --- a/srsenb/hdr/stack/rrc/rrc_nr.h +++ b/srsenb/hdr/stack/rrc/rrc_nr.h @@ -24,6 +24,8 @@ #include "srsran/common/threads.h" #include "srsran/common/timeout.h" #include "srsran/interfaces/gnb_interfaces.h" +#include "srsran/interfaces/gnb_ngap_interfaces.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" #include #include diff --git a/srsenb/hdr/stack/upper/ngap.h b/srsenb/hdr/stack/upper/ngap.h new file mode 100644 index 000000000..3962b88a7 --- /dev/null +++ b/srsenb/hdr/stack/upper/ngap.h @@ -0,0 +1,134 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#ifndef SRSENB_NGAP_H +#define SRSENB_NGAP_H + +#include "srsenb/hdr/common/common_enb.h" +#include "srsran/adt/circular_map.h" +#include "srsran/asn1/asn1_utils.h" +#include "srsran/asn1/ngap.h" +#include "srsran/common/bcd_helpers.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/network_utils.h" +#include "srsran/common/stack_procedure.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/task_scheduler.h" +#include "srsran/common/threads.h" +#include "srsran/interfaces/gnb_ngap_interfaces.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" +#include "srsran/srslog/srslog.h" + +namespace srsenb { +class ngap : public ngap_interface_rrc_nr +{ +public: + ngap(srsran::task_sched_handle task_sched_, + srslog::basic_logger& logger, + srsran::socket_manager_itf* rx_socket_handler); + int init(ngap_args_t args_, rrc_interface_ngap_nr* rrc_); + void stop(); + + // RRC NR interface + void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap_nr::rrcestablishment_cause_e cause, + srsran::unique_byte_buffer_t pdu){}; + void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap_nr::rrcestablishment_cause_e cause, + srsran::unique_byte_buffer_t pdu, + uint32_t m_tmsi, + uint8_t mmec){}; + void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu){}; + bool user_exists(uint16_t rnti) { return true; }; + void user_mod(uint16_t old_rnti, uint16_t new_rnti){}; + bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) { return true; }; + bool is_amf_connected(); + + /// TS 36.413, 8.3.1 - Initial Context Setup + void ue_ctxt_setup_complete(uint16_t rnti){}; + + // Stack interface + bool + handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); + +private: + static const int AMF_PORT = 38412; + static const int ADDR_FAMILY = AF_INET; + static const int SOCK_TYPE = SOCK_STREAM; + static const int PROTO = IPPROTO_SCTP; + static const int PPID = 60; + static const int NONUE_STREAM_ID = 0; + + // args + rrc_interface_ngap_nr* rrc = nullptr; + ngap_args_t args; + srslog::basic_logger& logger; + srsran::task_sched_handle task_sched; + srsran::task_queue_handle amf_task_queue; + srsran::socket_manager_itf* rx_socket_handler; + + srsran::unique_socket amf_socket; + struct sockaddr_in amf_addr = {}; // AMF address + bool amf_connected = false; + bool running = false; + uint32_t next_enb_ue_ngap_id = 1; // Next ENB-side UE identifier + uint16_t next_ue_stream_id = 1; // Next UE SCTP stream identifier + srsran::unique_timer amf_connect_timer, ngsetup_timeout; + + // Protocol IEs sent with every UL NGAP message + asn1::ngap_nr::tai_s tai; + asn1::ngap_nr::nr_cgi_s nr_cgi; + + asn1::ngap_nr::ng_setup_resp_s ngsetupresponse; + + // procedures + class ng_setup_proc_t + { + public: + struct ngsetupresult { + bool success = false; + enum class cause_t { timeout, failure } cause; + }; + + explicit ng_setup_proc_t(ngap* ngap_) : ngap_ptr(ngap_) {} + srsran::proc_outcome_t init(); + srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } + srsran::proc_outcome_t react(const ngsetupresult& event); + void then(const srsran::proc_state_t& result) const; + const char* name() const { return "AMF Connection"; } + + private: + srsran::proc_outcome_t start_amf_connection(); + + ngap* ngap_ptr = nullptr; + }; + + void build_tai_cgi(); + bool connect_amf(); + bool setup_ng(); + bool sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name); + + bool handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu); + bool handle_successfuloutcome(const asn1::ngap_nr::successful_outcome_s& msg); + + bool handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg); + + srsran::proc_t ngsetup_proc; + + std::string get_cause(const asn1::ngap_nr::cause_c& c); + void log_ngap_msg(const asn1::ngap_nr::ngap_pdu_c& msg, srsran::const_span sdu, bool is_rx); +}; + +} // namespace srsenb +#endif diff --git a/srsenb/src/stack/upper/CMakeLists.txt b/srsenb/src/stack/upper/CMakeLists.txt index 226bd4eb1..9ec6d3bbe 100644 --- a/srsenb/src/stack/upper/CMakeLists.txt +++ b/srsenb/src/stack/upper/CMakeLists.txt @@ -9,5 +9,5 @@ set(SOURCES gtpu.cc pdcp.cc rlc.cc s1ap.cc) add_library(srsenb_upper STATIC ${SOURCES}) -set(SOURCES pdcp_nr.cc rlc_nr.cc sdap.cc) +set(SOURCES pdcp_nr.cc rlc_nr.cc sdap.cc ngap.cc) add_library(srsgnb_upper STATIC ${SOURCES}) diff --git a/srsenb/src/stack/upper/ngap.cc b/srsenb/src/stack/upper/ngap.cc new file mode 100644 index 000000000..6aba77892 --- /dev/null +++ b/srsenb/src/stack/upper/ngap.cc @@ -0,0 +1,405 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/stack/upper/ngap.h" + +#define procError(fmt, ...) ngap_ptr->logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procWarning(fmt, ...) ngap_ptr->logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procInfo(fmt, ...) ngap_ptr->logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) + +using namespace asn1::ngap_nr; + +namespace srsenb { +/********************************************************* + * AMF Connection + *********************************************************/ + +srsran::proc_outcome_t ngap::ng_setup_proc_t::init() +{ + procInfo("Starting new AMF connection."); + return start_amf_connection(); +} + +srsran::proc_outcome_t ngap::ng_setup_proc_t::start_amf_connection() +{ + if (not ngap_ptr->running) { + procInfo("NGAP is not running anymore."); + return srsran::proc_outcome_t::error; + } + if (ngap_ptr->amf_connected) { + procInfo("gNB NGAP is already connected to AMF"); + return srsran::proc_outcome_t::success; + } + + if (not ngap_ptr->connect_amf()) { + procInfo("Could not connect to AMF"); + return srsran::proc_outcome_t::error; + } + + if (not ngap_ptr->setup_ng()) { + procError("NG setup failed. Exiting..."); + srsran::console("NG setup failed\n"); + ngap_ptr->running = false; + return srsran::proc_outcome_t::error; + } + + ngap_ptr->ngsetup_timeout.run(); + procInfo("NGSetupRequest sent. Waiting for response..."); + return srsran::proc_outcome_t::yield; +} + +srsran::proc_outcome_t ngap::ng_setup_proc_t::react(const srsenb::ngap::ng_setup_proc_t::ngsetupresult& event) +{ + if (ngap_ptr->ngsetup_timeout.is_running()) { + ngap_ptr->ngsetup_timeout.stop(); + } + if (event.success) { + procInfo("NGSetup procedure completed successfully"); + return srsran::proc_outcome_t::success; + } + procError("NGSetup failed."); + srsran::console("NGsetup failed\n"); + return srsran::proc_outcome_t::error; +} + +void ngap::ng_setup_proc_t::then(const srsran::proc_state_t& result) const +{ + if (result.is_error()) { + procInfo("Failed to initiate NG connection. Attempting reconnection in %d seconds", + ngap_ptr->amf_connect_timer.duration() / 1000); + srsran::console("Failed to initiate NG connection. Attempting reconnection in %d seconds\n", + ngap_ptr->amf_connect_timer.duration() / 1000); + ngap_ptr->rx_socket_handler->remove_socket(ngap_ptr->amf_socket.get_socket()); + ngap_ptr->amf_socket.close(); + procInfo("NGAP socket closed."); + ngap_ptr->amf_connect_timer.run(); + // Try again with in 10 seconds + } +} + +/********************************************************* + * NGAP class + *********************************************************/ + +ngap::ngap(srsran::task_sched_handle task_sched_, + srslog::basic_logger& logger, + srsran::socket_manager_itf* rx_socket_handler_) : + ngsetup_proc(this), logger(logger), task_sched(task_sched_), rx_socket_handler(rx_socket_handler_) +{ + amf_task_queue = task_sched.make_task_queue(); +} + +int ngap::init(ngap_args_t args_, rrc_interface_ngap_nr* rrc_) +{ + rrc = rrc_; + args = args_; + + build_tai_cgi(); + + // Setup AMF reconnection timer + amf_connect_timer = task_sched.get_unique_timer(); + auto amf_connect_run = [this](uint32_t tid) { + if (ngsetup_proc.is_busy()) { + logger.error("Failed to initiate NGSetup procedure."); + } + ngsetup_proc.launch(); + }; + amf_connect_timer.set(10000, amf_connect_run); + // Setup NGSetup timeout + ngsetup_timeout = task_sched.get_unique_timer(); + uint32_t ngsetup_timeout_val = 5000; + ngsetup_timeout.set(ngsetup_timeout_val, [this](uint32_t tid) { + ng_setup_proc_t::ngsetupresult res; + res.success = false; + res.cause = ng_setup_proc_t::ngsetupresult::cause_t::timeout; + ngsetup_proc.trigger(res); + }); + + running = true; + // starting AMF connection + if (not ngsetup_proc.launch()) { + logger.error("Failed to initiate NGSetup procedure."); + } + + return SRSRAN_SUCCESS; +} + +void ngap::stop() +{ + running = false; + amf_socket.close(); +} + +bool ngap::is_amf_connected() +{ + return amf_connected; +} + +// Generate common NGAP protocol IEs from config args +void ngap::build_tai_cgi() +{ + uint32_t plmn; + + // TAI + srsran::s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + tai.plmn_id.from_number(plmn); + + tai.tac.from_number(args.tac); + + // nr_cgi + nr_cgi.plmn_id.from_number(plmn); + // TODO Check how to build nr cell id + nr_cgi.nrcell_id.from_number((uint32_t)(args.gnb_id << 8) | args.cell_id); +} + +/******************************************************************************* +/* NGAP message handlers +********************************************************************************/ +bool ngap::handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, + const sockaddr_in& from, + const sctp_sndrcvinfo& sri, + int flags) +{ + // Handle Notification Case + if (flags & MSG_NOTIFICATION) { + // Received notification + union sctp_notification* notification = (union sctp_notification*)pdu->msg; + logger.debug("SCTP Notification %d", notification->sn_header.sn_type); + if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) { + logger.info("SCTP Association Shutdown. Association: %d", sri.sinfo_assoc_id); + srsran::console("SCTP Association Shutdown. Association: %d\n", sri.sinfo_assoc_id); + rx_socket_handler->remove_socket(amf_socket.get_socket()); + amf_socket.close(); + } else if (notification->sn_header.sn_type == SCTP_PEER_ADDR_CHANGE && + notification->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) { + logger.info("SCTP peer addres unreachable. Association: %d", sri.sinfo_assoc_id); + srsran::console("SCTP peer address unreachable. Association: %d\n", sri.sinfo_assoc_id); + rx_socket_handler->remove_socket(amf_socket.get_socket()); + amf_socket.close(); + } + } else if (pdu->N_bytes == 0) { + logger.error("SCTP return 0 bytes. Closing socket"); + amf_socket.close(); + } + + // Restart MME connection procedure if we lost connection + if (not amf_socket.is_open()) { + amf_connected = false; + if (ngsetup_proc.is_busy()) { + logger.error("Failed to initiate MME connection procedure, as it is already running."); + return false; + } + ngsetup_proc.launch(); + return false; + } + + handle_ngap_rx_pdu(pdu.get()); + return true; +} + +bool ngap::handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu) +{ + // Save message to PCAP + // if (pcap != nullptr) { + // pcap->write_ngap(pdu->msg, pdu->N_bytes); + // } + + ngap_pdu_c rx_pdu; + asn1::cbit_ref bref(pdu->msg, pdu->N_bytes); + + if (rx_pdu.unpack(bref) != asn1::SRSASN_SUCCESS) { + logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack received PDU"); + cause_c cause; + cause.set_protocol().value = cause_protocol_opts::transfer_syntax_error; + // send_error_indication(cause); + return false; + } + // log_ngap_msg(rx_pdu, srsran::make_span(*pdu), true); + + switch (rx_pdu.type().value) { + // case ngap_pdu_c::types_opts::init_msg: + // return handle_initiatingmessage(rx_pdu.init_msg()); + case ngap_pdu_c::types_opts::successful_outcome: + return handle_successfuloutcome(rx_pdu.successful_outcome()); + // case ngap_pdu_c::types_opts::unsuccessful_outcome: + // return handle_unsuccessfuloutcome(rx_pdu.unsuccessful_outcome()); + default: + logger.error("Unhandled PDU type %d", rx_pdu.type().value); + return false; + } + + return true; +} + +bool ngap::handle_successfuloutcome(const successful_outcome_s& msg) +{ + switch (msg.value.type().value) { + case ngap_elem_procs_o::successful_outcome_c::types_opts::ng_setup_resp: + return handle_ngsetupresponse(msg.value.ng_setup_resp()); + default: + logger.error("Unhandled successful outcome message: %s", msg.value.type().to_string()); + } + return true; +} + +bool ngap::handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg) +{ + ngsetupresponse = msg; + amf_connected = true; + ng_setup_proc_t::ngsetupresult res; + res.success = true; + ngsetup_proc.trigger(res); + return true; +} + +/******************************************************************************* +/* NGAP connection helpers +********************************************************************************/ + +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_client(&amf_socket, socket_type::seqpacket, args.ngc_bind_addr.c_str())) { + return false; + } + logger.info("SCTP socket opened. fd=%d", amf_socket.fd()); + + // Connect to the AMF address + if (not amf_socket.connect_to(args.amf_addr.c_str(), AMF_PORT, &amf_addr)) { + return false; + } + logger.info("SCTP socket connected with AMF. fd=%d", amf_socket.fd()); + + // Assign a handler to rx AMF packets + auto rx_callback = + [this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags) { + // Defer the handling of AMF packet to eNB stack main thread + handle_amf_rx_msg(std::move(pdu), from, sri, flags); + }; + rx_socket_handler->add_socket_handler(amf_socket.fd(), + srsran::make_sctp_sdu_handler(logger, amf_task_queue, rx_callback)); + + logger.info("SCTP socket established with AMF"); + return true; +} + +bool ngap::setup_ng() +{ + uint32_t tmp32; + uint16_t tmp16; + + uint32_t plmn; + srsran::s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + plmn = htonl(plmn); + + ngap_pdu_c pdu; + pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_NG_SETUP); + ng_setup_request_ies_container& container = pdu.init_msg().value.ng_setup_request().protocol_ies; + global_gnb_id_s& global_gnb_id = container.global_ran_node_id.value.set_global_gnb_id(); + global_gnb_id.plmn_id = tai.plmn_id; + // TODO: when ASN1 is fixed + // global_gnb_id.gnb_id.set_gnb_id().from_number(args.gnb_id); + + // container.ran_node_name_present = true; + // container.ran_node_name.value.from_string(args.gnb_name); + + asn1::bounded_bitstring<22, 32, false, true>& gnb_str = global_gnb_id.gnb_id.set_gnb_id(); + gnb_str.resize(32); + uint8_t buffer[4]; + asn1::bit_ref bref(&buffer[0], sizeof(buffer)); + bref.pack(args.gnb_id, 8); + memcpy(gnb_str.data(), &buffer[0], bref.distance_bytes()); + + // .from_number(args.gnb_id); + + container.ran_node_name_present = true; + if (args.gnb_name.length() >= 150) { + args.gnb_name.resize(150); + } + // container.ran_node_name.value.from_string(args.enb_name); + container.ran_node_name.value.resize(args.gnb_name.size()); + memcpy(&container.ran_node_name.value[0], &args.gnb_name[0], args.gnb_name.size()); + + container.supported_ta_list.value.resize(1); + container.supported_ta_list.value[0].tac = tai.tac; + container.supported_ta_list.value[0].broadcast_plmn_list.resize(1); + container.supported_ta_list.value[0].broadcast_plmn_list[0].plmn_id = tai.plmn_id; + container.supported_ta_list.value[0].broadcast_plmn_list[0].tai_slice_support_list.resize(1); + container.supported_ta_list.value[0].broadcast_plmn_list[0].tai_slice_support_list[0].s_nssai.sst.from_number(1); + + container.default_paging_drx.value.value = asn1::ngap_nr::paging_drx_opts::v256; // Todo: add to args, config file + + return sctp_send_ngap_pdu(pdu, 0, "ngSetupRequest"); +} + +/******************************************************************************* +/* General helpers +********************************************************************************/ + +bool ngap::sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name) +{ + if (not amf_connected and rnti != SRSRAN_INVALID_RNTI) { + logger.error("Aborting %s for rnti=0x%x. Cause: AMF is not connected.", procedure_name, rnti); + return false; + } + + srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer(); + if (buf == nullptr) { + logger.error("Fatal Error: Couldn't allocate buffer for %s.", procedure_name); + return false; + } + asn1::bit_ref bref(buf->msg, buf->get_tailroom()); + if (tx_pdu.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack TX PDU %s", procedure_name); + return false; + } + buf->N_bytes = bref.distance_bytes(); + + // TODO: when we got pcap support + // Save message to PCAP + // if (pcap != nullptr) { + // pcap->write_s1ap(buf->msg, buf->N_bytes); + // } + + if (rnti != SRSRAN_INVALID_RNTI) { + logger.info(buf->msg, buf->N_bytes, "Tx S1AP SDU, %s, rnti=0x%x", procedure_name, rnti); + } else { + logger.info(buf->msg, buf->N_bytes, "Tx S1AP SDU, %s", procedure_name); + } + // TODO: when user list is ready + // uint16_t streamid = rnti == SRSRAN_INVALID_RNTI ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id; + uint16_t streamid = 0; + ssize_t n_sent = sctp_sendmsg(amf_socket.fd(), + buf->msg, + buf->N_bytes, + (struct sockaddr*)&amf_addr, + sizeof(struct sockaddr_in), + htonl(PPID), + 0, + streamid, + 0, + 0); + if (n_sent == -1) { + if (rnti != SRSRAN_INVALID_RNTI) { + logger.error("Error: Failure at Tx S1AP SDU, %s, rnti=0x%x", procedure_name, rnti); + } else { + logger.error("Error: Failure at Tx S1AP SDU, %s", procedure_name); + } + return false; + } + return true; +} + +} // namespace srsenb