diff --git a/lib/include/srsran/common/ngap_pcap.h b/lib/include/srsran/common/ngap_pcap.h new file mode 100644 index 000000000..434a0a271 --- /dev/null +++ b/lib/include/srsran/common/ngap_pcap.h @@ -0,0 +1,43 @@ +/** + * + * \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_PCAP_H +#define SRSRAN_NGAP_PCAP_H + +#include "srsran/common/pcap.h" +#include + +namespace srsran { + +class ngap_pcap +{ +public: + ngap_pcap(); + ngap_pcap(const ngap_pcap& other) = delete; + ngap_pcap& operator=(const ngap_pcap& other) = delete; + ngap_pcap(ngap_pcap&& other) = delete; + ngap_pcap& operator=(ngap_pcap&& other) = delete; + + void enable(); + void open(const char* filename_); + void close(); + void write_ngap(uint8_t* pdu, uint32_t pdu_len_bytes); + +private: + bool enable_write = false; + std::string filename; + FILE* pcap_file = nullptr; +}; + +} // namespace srsran + +#endif // SRSRAN_NAS_PCAP_H diff --git a/lib/include/srsran/common/pcap.h b/lib/include/srsran/common/pcap.h index 10bcef01d..8bc33879f 100644 --- a/lib/include/srsran/common/pcap.h +++ b/lib/include/srsran/common/pcap.h @@ -24,6 +24,7 @@ #define UDP_DLT 149 // UDP needs to be selected as protocol #define S1AP_LTE_DLT 150 #define NAS_5G_DLT 151 +#define NGAP_5G_DLT 152 /* This structure gets written to the start of the file */ typedef struct pcap_hdr_s { @@ -180,6 +181,12 @@ typedef struct S1AP_Context_Info_s { unsigned char dummy; } S1AP_Context_Info_t; +/* Context information for every S1AP PDU that will be logged */ +typedef struct NGAP_Context_Info_s { + // No Context yet + unsigned char dummy; +} NGAP_Context_Info_t; + #ifdef __cplusplus extern "C" { #endif @@ -204,6 +211,9 @@ int LTE_PCAP_RLC_WritePDU(FILE* fd, RLC_Context_Info_t* context, const unsigned /* Write an individual S1AP PDU (PCAP packet header + s1ap-context + s1ap-pdu) */ int LTE_PCAP_S1AP_WritePDU(FILE* fd, S1AP_Context_Info_t* context, const unsigned char* PDU, unsigned int length); +/* Write an individual S1AP PDU (PCAP packet header + s1ap-context + s1ap-pdu) */ +int LTE_PCAP_NGAP_WritePDU(FILE* fd, NGAP_Context_Info_t* context, const unsigned char* PDU, unsigned int length); + /* Write an individual NR MAC PDU (PCAP packet header + UDP header + nr-mac-context + mac-pdu) */ int NR_PCAP_MAC_UDP_WritePDU(FILE* fd, mac_nr_context_info_t* context, const unsigned char* PDU, unsigned int length); int NR_PCAP_PACK_MAC_CONTEXT_TO_BUFFER(mac_nr_context_info_t* context, uint8_t* buffer, unsigned int length); diff --git a/lib/src/common/CMakeLists.txt b/lib/src/common/CMakeLists.txt index 811f25b9e..1ddf87b07 100644 --- a/lib/src/common/CMakeLists.txt +++ b/lib/src/common/CMakeLists.txt @@ -27,6 +27,7 @@ set(SOURCES arch_select.cc rrc_common.cc rlc_pcap.cc s1ap_pcap.cc + ngap_pcap.cc security.cc standard_streams.cc thread_pool.cc diff --git a/lib/src/common/ngap_pcap.cc b/lib/src/common/ngap_pcap.cc new file mode 100644 index 000000000..34ce35d2d --- /dev/null +++ b/lib/src/common/ngap_pcap.cc @@ -0,0 +1,61 @@ +/** + * + * \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 "srsran/common/ngap_pcap.h" +#include "srsran/common/pcap.h" +#include "srsran/srsran.h" +#include "srsran/support/emergency_handlers.h" +#include + +namespace srsran { + +/// Try to flush the contents of the pcap class before the application is killed. +static void emergency_cleanup_handler(void* data) +{ + reinterpret_cast(data)->close(); +} + +ngap_pcap::ngap_pcap() +{ + add_emergency_cleanup_handler(emergency_cleanup_handler, this); +} + +void ngap_pcap::enable() +{ + enable_write = true; +} +void ngap_pcap::open(const char* filename_) +{ + filename = filename_; + pcap_file = DLT_PCAP_Open(NGAP_5G_DLT, filename.c_str()); + enable_write = true; +} +void ngap_pcap::close() +{ + if (!enable_write) { + return; + } + fprintf(stdout, "Saving NGAP PCAP file (DLT=%d) to %s\n", NGAP_5G_DLT, filename.c_str()); + DLT_PCAP_Close(pcap_file); +} + +void ngap_pcap::write_ngap(uint8_t* pdu, uint32_t pdu_len_bytes) +{ + if (enable_write) { + NGAP_Context_Info_t context; + if (pdu) { + LTE_PCAP_NGAP_WritePDU(pcap_file, &context, pdu, pdu_len_bytes); + } + } +} + +} // namespace srsran diff --git a/lib/src/common/pcap.c b/lib/src/common/pcap.c index fcb44f0bf..ea454cd3b 100644 --- a/lib/src/common/pcap.c +++ b/lib/src/common/pcap.c @@ -336,6 +336,34 @@ int LTE_PCAP_S1AP_WritePDU(FILE* fd, S1AP_Context_Info_t* context, const unsigne return 1; } +/* Write an individual PDU (PCAP packet header + s1ap-context + s1ap-pdu) */ +int LTE_PCAP_NGAP_WritePDU(FILE* fd, NGAP_Context_Info_t* context, const unsigned char* PDU, unsigned int length) +{ + pcaprec_hdr_t packet_header; + + /* Can't write if file wasn't successfully opened */ + if (fd == NULL) { + printf("Error: Can't write to empty file handle\n"); + return 0; + } + + /****************************************************************/ + /* PCAP Header */ + struct timeval t; + gettimeofday(&t, NULL); + packet_header.ts_sec = t.tv_sec; + packet_header.ts_usec = t.tv_usec; + packet_header.incl_len = length; + packet_header.orig_len = length; + + /***************************************************************/ + /* Now write everything to the file */ + fwrite(&packet_header, sizeof(pcaprec_hdr_t), 1, fd); + fwrite(PDU, 1, length, fd); + + return 1; +} + /************************************************************************** * API functions for writing MAC-NR PCAP files * **************************************************************************/ @@ -443,4 +471,4 @@ int NR_PCAP_MAC_UDP_WritePDU(FILE* fd, mac_nr_context_info_t* context, const uns fwrite(PDU, 1, length, fd); return 1; -} \ No newline at end of file +} diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index d31d959f9..dc9409539 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -137,6 +137,8 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("pcap.nr_filename", bpo::value(&args->nr_stack.mac.pcap.filename)->default_value("/tmp/enb_mac_nr.pcap"), "NR MAC layer capture filename") ("pcap.s1ap_enable", bpo::value(&args->stack.s1ap_pcap.enable)->default_value(false), "Enable S1AP packet captures for wireshark") ("pcap.s1ap_filename", bpo::value(&args->stack.s1ap_pcap.filename)->default_value("/tmp/enb_s1ap.pcap"), "S1AP layer capture filename") + ("pcap.ngap_enable", bpo::value(&args->nr_stack.ngap_pcap.enable)->default_value(false), "Enable NGAP packet captures for wireshark") + ("pcap.ngap_filename", bpo::value(&args->nr_stack.ngap_pcap.filename)->default_value("/tmp/enb_ngap.pcap"), "NGAP layer capture filename") ("pcap.mac_net_enable", bpo::value(&args->stack.mac_pcap_net.enable)->default_value(false), "Enable MAC network captures") ("pcap.bind_ip", bpo::value(&args->stack.mac_pcap_net.bind_ip)->default_value("0.0.0.0"), "Bind IP address for MAC network trace") ("pcap.bind_port", bpo::value(&args->stack.mac_pcap_net.bind_port)->default_value(5687), "Bind port for MAC network trace") @@ -260,6 +262,14 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("scheduler.nr_pusch_mcs", bpo::value(&args->nr_stack.mac.sched_cfg.fixed_ul_mcs)->default_value(28), "Fixed NR UL MCS (-1 for dynamic).") ("expert.nr_pusch_max_its", bpo::value(&args->phy.nr_pusch_max_its)->default_value(10), "Maximum number of LDPC iterations for NR.") + //NGAP section + ("gnb.enb_id", bpo::value(&args->nr_stack.ngap.gnb_id)->default_value(0), "gNodeB ID") + ("gnb.name", bpo::value(&args->nr_stack.ngap.gnb_name)->default_value("srsgnb01"), "gNodeB Name") + ("gnb.amf_addr", bpo::value(&args->nr_stack.ngap.amf_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") + ("gnb.gtp_bind_addr", bpo::value(&args->nr_stack.ngap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") + ("gnb.gtp_advertise_addr", bpo::value(&args->nr_stack.ngap.gtp_advertise_addr)->default_value(""), "IP address of eNB to advertise for DL GTP-U Traffic") + ("gnb.ngc_bind_addr", bpo::value(&args->nr_stack.ngap.ngc_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") + // VNF params ("vnf.type", bpo::value(&args->phy.vnf_args.type)->default_value("gnb"), "VNF instance type [gnb,ue].") ("vnf.addr", bpo::value(&args->phy.vnf_args.bind_addr)->default_value("localhost"), "Address to bind VNF interface.") @@ -336,6 +346,12 @@ void parse_args(all_args_t* args, int argc, char* argv[]) if (!srsran::string_to_mnc(mnc, &args->stack.s1ap.mnc)) { cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl; } + if (!srsran::string_to_mcc(mcc, &args->nr_stack.ngap.mcc)) { + cout << "Error parsing enb.mcc:" << mcc << " - must be a 3-digit string." << endl; + } + if (!srsran::string_to_mnc(mnc, &args->nr_stack.ngap.mnc)) { + cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl; + } if (args->stack.embms.enable) { if (args->stack.mac.sched.max_nof_ctrl_symbols == 3) { diff --git a/srsgnb/hdr/stack/gnb_stack_nr.h b/srsgnb/hdr/stack/gnb_stack_nr.h index 85d68bc1f..57bebcc1e 100644 --- a/srsgnb/hdr/stack/gnb_stack_nr.h +++ b/srsgnb/hdr/stack/gnb_stack_nr.h @@ -27,6 +27,8 @@ #include "srsenb/hdr/stack/enb_stack_base.h" #include "srsran/interfaces/gnb_interfaces.h" +#include "srsran/common/ngap_pcap.h" + namespace srsenb { class ngap; @@ -36,6 +38,7 @@ struct gnb_stack_args_t { stack_log_args_t log; mac_nr_args_t mac; ngap_args_t ngap; + pcap_args_t ngap_pcap; }; class gnb_stack_nr final : public srsenb::enb_stack_base, @@ -129,6 +132,8 @@ private: srslog::basic_logger& gtpu_logger; srslog::basic_logger& stack_logger; + srsran::ngap_pcap ngap_pcap; + // task scheduling static const int STACK_MAIN_THREAD_PRIO = 4; srsran::task_scheduler task_sched; diff --git a/srsgnb/hdr/stack/ngap/ngap.h b/srsgnb/hdr/stack/ngap/ngap.h index ac106b755..3c5417afa 100644 --- a/srsgnb/hdr/stack/ngap/ngap.h +++ b/srsgnb/hdr/stack/ngap/ngap.h @@ -22,6 +22,7 @@ #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" #include "srsran/common/network_utils.h" +#include "srsran/common/ngap_pcap.h" #include "srsran/common/stack_procedure.h" #include "srsran/common/standard_streams.h" #include "srsran/common/task_scheduler.h" @@ -70,10 +71,13 @@ public: // Stack interface bool - handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); + handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); void get_metrics(ngap_metrics_t& m); void get_args(ngap_args_t& args_); + // PCAP + void start_pcap(srsran::ngap_pcap* pcap_); + private: static const int AMF_PORT = 38412; static const int ADDR_FAMILY = AF_INET; @@ -128,6 +132,9 @@ private: // TS 38.413 - Section 9.2.1.1 - PDU Session Resource Setup Request bool handle_ue_pdu_session_res_setup_request(const asn1::ngap_nr::pdu_session_res_setup_request_s& msg); + // PCAP + srsran::ngap_pcap* pcap = nullptr; + class user_list { public: diff --git a/srsgnb/src/stack/gnb_stack_nr.cc b/srsgnb/src/stack/gnb_stack_nr.cc index 1cb69ecd9..7524fc516 100644 --- a/srsgnb/src/stack/gnb_stack_nr.cc +++ b/srsgnb/src/stack/gnb_stack_nr.cc @@ -104,6 +104,11 @@ int gnb_stack_nr::init(const gnb_stack_args_t& args_, gtpu_args.mme_addr = args.ngap.amf_addr; gtpu_args.gtp_bind_addr = args.ngap.gtp_bind_addr; gtpu->init(gtpu_args, &pdcp); + + if (args.ngap_pcap.enable) { + ngap_pcap.open(args.ngap_pcap.filename.c_str()); + ngap->start_pcap(&ngap_pcap); + } } // TODO: add SDAP @@ -231,4 +236,4 @@ void gnb_stack_nr::rach_detected(const rach_info_t& rach_info) mac.rach_detected(rach_info); } -} // namespace srsenb \ No newline at end of file +} // namespace srsenb diff --git a/srsgnb/src/stack/ngap/ngap.cc b/srsgnb/src/stack/ngap/ngap.cc index fa53daff2..3da0f86e9 100644 --- a/srsgnb/src/stack/ngap/ngap.cc +++ b/srsgnb/src/stack/ngap/ngap.cc @@ -371,11 +371,10 @@ bool ngap::handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, bool ngap::handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu) { - // TODO: // Save message to PCAP - // if (pcap != nullptr) { - // pcap->write_ngap(pdu->msg, pdu->N_bytes); - // } + 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); @@ -696,6 +695,11 @@ bool ngap::sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t } buf->N_bytes = bref.distance_bytes(); + // Save message to PCAP + if (pcap != nullptr) { + pcap->write_ngap(buf->msg, buf->N_bytes); + } + if (rnti != SRSRAN_INVALID_RNTI) { logger.info(buf->msg, buf->N_bytes, "Tx NGAP SDU, %s, rnti=0x%x", procedure_name, rnti); } else { @@ -794,4 +798,11 @@ std::string ngap::get_cause(const cause_c& c) return cause; } +/* + * PCAP + */ +void ngap::start_pcap(srsran::ngap_pcap* pcap_) +{ + pcap = pcap_; +} } // namespace srsenb