From 894e4d3501242966135f9617c25d6976f6b442ea Mon Sep 17 00:00:00 2001 From: David Rupprecht Date: Fri, 26 Feb 2021 14:32:57 +0100 Subject: [PATCH] Added mac pcap network class that dumps the packets to a udp network sink --- lib/include/srslte/common/mac_pcap.h | 5 +- lib/include/srslte/common/mac_pcap_net.h | 45 ++++++ lib/src/common/CMakeLists.txt | 3 +- lib/src/common/mac_pcap_net.cc | 187 +++++++++++++++++++++++ lib/test/common/CMakeLists.txt | 3 + lib/test/common/mac_pcap_net_test.cc | 115 ++++++++++++++ 6 files changed, 355 insertions(+), 3 deletions(-) create mode 100644 lib/include/srslte/common/mac_pcap_net.h create mode 100644 lib/src/common/mac_pcap_net.cc create mode 100644 lib/test/common/mac_pcap_net_test.cc diff --git a/lib/include/srslte/common/mac_pcap.h b/lib/include/srslte/common/mac_pcap.h index a1f740976..c556f8250 100644 --- a/lib/include/srslte/common/mac_pcap.h +++ b/lib/include/srslte/common/mac_pcap.h @@ -27,11 +27,12 @@ public: uint32_t close(); private: - void write_pdu(srslte::mac_pcap_base::pcap_pdu_t& pdu); + void write_pdu(srslte::mac_pcap_base::pcap_pdu_t& pdu); + void run_thread() final; + FILE* pcap_file = nullptr; uint32_t dlt = 0; // The DLT used for the PCAP file std::string filename; - void run_thread() final; }; } // namespace srslte diff --git a/lib/include/srslte/common/mac_pcap_net.h b/lib/include/srslte/common/mac_pcap_net.h new file mode 100644 index 000000000..1f38e7407 --- /dev/null +++ b/lib/include/srslte/common/mac_pcap_net.h @@ -0,0 +1,45 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2020 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 SRSLTE_MAC_PCAP_NET_H +#define SRSLTE_MAC_PCAP_NET_H + +#include "srslte/common/common.h" +#include "srslte/common/mac_pcap_base.h" +#include "srslte/common/network_utils.h" +#include "srslte/srslte.h" + +namespace srslte { +class mac_pcap_net : public mac_pcap_base +{ +public: + mac_pcap_net(); + ~mac_pcap_net(); + uint32_t open(std::string client_ip_addr_, + std::string bind_addr_str = "0.0.0.0", + uint16_t client_udp_port_ = 5847, + uint16_t bind_udp_port_ = 5687, + uint32_t ue_id_ = 0); + uint32_t close(); + +private: + void write_pdu(srslte::mac_pcap_base::pcap_pdu_t& pdu); + void write_mac_lte_pdu_to_net(srslte::mac_pcap_base::pcap_pdu_t& pdu); + void write_mac_nr_pdu_to_net(srslte::mac_pcap_base::pcap_pdu_t& pdu); + void run_thread() final; + + srslte::socket_handler_t socket; + struct sockaddr_in client_addr; +}; +} // namespace srslte + +#endif // SRSLTE_MAC_PCAP_NET_H diff --git a/lib/src/common/CMakeLists.txt b/lib/src/common/CMakeLists.txt index 1b37fa44b..9d758c093 100644 --- a/lib/src/common/CMakeLists.txt +++ b/lib/src/common/CMakeLists.txt @@ -23,6 +23,7 @@ set(SOURCES arch_select.cc mac_pcap_base.cc nas_pcap.cc network_utils.cc + mac_pcap_net.cc pcap.c rlc_pcap.cc s1ap_pcap.cc @@ -47,7 +48,7 @@ add_dependencies(srslte_common gen_build_info) add_executable(arch_select arch_select.cc) target_include_directories(srslte_common PUBLIC ${SEC_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${BACKWARD_INCLUDE_DIRS}) -target_link_libraries(srslte_common srslte_phy srslog ${SEC_LIBRARIES} ${BACKWARD_LIBRARIES}) +target_link_libraries(srslte_common srslte_phy srslog ${SEC_LIBRARIES} ${BACKWARD_LIBRARIES} ${SCTP_LIBRARIES}) target_compile_definitions(srslte_common PRIVATE ${BACKWARD_DEFINITIONS}) INSTALL(TARGETS srslte_common DESTINATION ${LIBRARY_DIR}) diff --git a/lib/src/common/mac_pcap_net.cc b/lib/src/common/mac_pcap_net.cc new file mode 100644 index 000000000..027cfa844 --- /dev/null +++ b/lib/src/common/mac_pcap_net.cc @@ -0,0 +1,187 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2020 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 "srslte/common/mac_pcap_net.h" + +namespace srslte { + +mac_pcap_net::mac_pcap_net() : mac_pcap_base() {} + +mac_pcap_net::~mac_pcap_net() +{ + close(); +} +uint32_t mac_pcap_net::open(std::string client_ip_addr_, + std::string bind_addr_str, + uint16_t client_udp_port_, + uint16_t bind_udp_port_, + uint32_t ue_id_) +{ + std::lock_guard lock(mutex); + + if (socket.is_init()) { + logger.error("PCAP socket writer for %s already running. Close first.", bind_addr_str.c_str()); + return SRSLTE_ERROR; + } + + if (not socket.open_socket( + net_utils::addr_family::ipv4, net_utils::socket_type::datagram, net_utils::protocol_type::UDP)) { + logger.error("Couldn't open socket %s to write PCAP", bind_addr_str.c_str()); + return SRSLTE_ERROR; + } + if (not socket.bind_addr(bind_addr_str.c_str(), bind_udp_port_)) { + socket.reset(); + logger.error("Couldn't bind socket %s to write PCAP", bind_addr_str.c_str()); + return SRSLTE_ERROR; + } + + client_addr.sin_family = AF_INET; + client_addr.sin_addr.s_addr = inet_addr(client_ip_addr_.c_str()); + client_addr.sin_port = htons(client_udp_port_); + running = true; + ue_id = ue_id_; + // start writer thread + start(); + + return SRSLTE_SUCCESS; +} + +uint32_t mac_pcap_net::close() +{ + { + std::lock_guard lock(mutex); + if (running == false || socket.is_init() == false) { + return SRSLTE_ERROR; + } + + // tell writer thread to stop + running = false; + pcap_pdu_t pdu = {}; + queue.push(std::move(pdu)); + } + + wait_thread_finish(); + // close socket handle + if (socket.is_init()) { + std::lock_guard lock(mutex); + socket.close(); + } + + return SRSLTE_SUCCESS; +} + +void mac_pcap_net::run_thread() +{ + // blocking write until stopped + while (running) { + pcap_pdu_t pdu = queue.wait_pop(); + { + std::lock_guard lock(mutex); + write_pdu(pdu); + } + } + + // write remainder of queue + std::lock_guard lock(mutex); + pcap_pdu_t pdu = {}; + while (queue.try_pop(&pdu)) { + write_pdu(pdu); + } +} + +void mac_pcap_net::write_pdu(pcap_pdu_t& pdu) +{ + if (pdu.pdu != nullptr && socket.is_init()) { + switch (pdu.rat) { + case srslte_rat_t::lte: + write_mac_lte_pdu_to_net(pdu); + break; + case srslte_rat_t::nr: + write_mac_nr_pdu_to_net(pdu); + break; + default: + logger.error("Error writing PDU to PCAP socket. Unsupported RAT selected."); + } + } +} + +void mac_pcap_net::write_mac_lte_pdu_to_net(pcap_pdu_t& pdu) +{ + int bytes_sent; + uint32_t offset = 0; + uint8_t buffer[PCAP_CONTEXT_HEADER_MAX]; + + // MAC_LTE_START_STRING for UDP heuristics + memcpy(buffer + offset, MAC_LTE_START_STRING, strlen(MAC_LTE_START_STRING)); + offset += strlen(MAC_LTE_START_STRING); + + offset += LTE_PCAP_PACK_MAC_CONTEXT_TO_BUFFER(&pdu.context, buffer + offset, PCAP_CONTEXT_HEADER_MAX); + + if (pdu.pdu.get()->get_headroom() < offset) { + logger.error("PDU headroom is to small for adding context buffer"); + return; + } + + if (pdu.pdu.get()->get_headroom() < offset) { + logger.error("PDU headroom is to small for adding context buffer"); + return; + } + + pdu.pdu.get()->msg -= offset; + memcpy(pdu.pdu.get()->msg, buffer, offset); + pdu.pdu.get()->N_bytes += offset; + + bytes_sent = sendto(socket.get_socket(), + pdu.pdu.get()->msg, + pdu.pdu.get()->N_bytes, + 0, + (const struct sockaddr*)&client_addr, + sizeof(client_addr)); + + if ((int)pdu.pdu.get()->N_bytes != bytes_sent) { + logger.error("Sending UDP packet mismatches %d != %d", pdu.pdu.get()->N_bytes, bytes_sent); + } +} + +void mac_pcap_net::write_mac_nr_pdu_to_net(pcap_pdu_t& pdu) +{ + int bytes_sent; + uint32_t offset = 0; + uint8_t buffer[PCAP_CONTEXT_HEADER_MAX]; + + // MAC_LTE_START_STRING for UDP heuristics + memcpy(buffer + offset, MAC_LTE_START_STRING, strlen(MAC_LTE_START_STRING)); + offset += strlen(MAC_LTE_START_STRING); + + offset += NR_PCAP_PACK_MAC_CONTEXT_TO_BUFFER(&pdu.context_nr, buffer + offset, PCAP_CONTEXT_HEADER_MAX); + + if (pdu.pdu.get()->get_headroom() < offset) { + logger.error("PDU headroom is to small for adding context buffer"); + return; + } + + pdu.pdu.get()->msg -= offset; + memcpy(pdu.pdu.get()->msg, buffer, offset); + pdu.pdu.get()->N_bytes += offset; + + bytes_sent = sendto(socket.get_socket(), + pdu.pdu.get()->msg, + pdu.pdu.get()->N_bytes, + 0, + (const struct sockaddr*)&client_addr, + sizeof(client_addr)); + + if ((int)pdu.pdu.get()->N_bytes != bytes_sent) { + logger.error("Sending UDP packet mismatches %d != %d", pdu.pdu.get()->N_bytes, bytes_sent); + } +} +} // namespace srslte \ No newline at end of file diff --git a/lib/test/common/CMakeLists.txt b/lib/test/common/CMakeLists.txt index f78d7b381..3c43defb3 100644 --- a/lib/test/common/CMakeLists.txt +++ b/lib/test/common/CMakeLists.txt @@ -88,3 +88,6 @@ target_link_libraries(pnf_dummy srslte_common ${CMAKE_THREAD_LIBS_INIT} ${Boost_ add_executable(pnf_bridge pnf_bridge.cc) target_link_libraries(pnf_bridge srslte_common ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) + +add_executable(mac_pcap_net_test mac_pcap_net_test.cc) +target_link_libraries(mac_pcap_net_test srslte_common ${SCTP_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) \ No newline at end of file diff --git a/lib/test/common/mac_pcap_net_test.cc b/lib/test/common/mac_pcap_net_test.cc new file mode 100644 index 000000000..94d11caf7 --- /dev/null +++ b/lib/test/common/mac_pcap_net_test.cc @@ -0,0 +1,115 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2020 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 "srslte/common/common.h" +#include "srslte/common/mac_pcap_net.h" +#include "srslte/common/test_common.h" +#include +#include + +// Write #num_pdus UL MAC PDUs using PCAP handle +void write_pcap_eutra_thread_function(srslte::mac_pcap_net* pcap_handle, + const std::array& pdu, + uint32_t num_pdus) +{ + for (uint32_t i = 0; i < num_pdus; i++) { + pcap_handle->write_ul_crnti(const_cast(pdu.data()), pdu.size(), 0x1001, true, 1, 0); + } + + std::cout << "Finished thread " << std::this_thread::get_id() << "\n"; +} + +// Write #num_pdus DL MAC NR PDUs using PCAP handle +void write_pcap_nr_thread_function(srslte::mac_pcap_net* pcap_handle, + const std::array& pdu, + uint32_t num_pdus) +{ + for (uint32_t i = 0; i < num_pdus; i++) { + pcap_handle->write_dl_crnti_nr(const_cast(pdu.data()), pdu.size(), 0x1001, 0, 1); + } + + std::cout << "Finished thread " << std::this_thread::get_id() << "\n"; +} + +int lte_mac_pcap_net_test() +{ + std::array tv = { + 0x21, 0x08, 0x22, 0x80, 0x82, 0x1f, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint32_t num_threads = 10; + uint32_t num_pdus_per_thread = 100; + + std::unique_ptr pcap_handle = std::unique_ptr(new srslte::mac_pcap_net()); + TESTASSERT(pcap_handle->open("127.0.0.1") == SRSLTE_SUCCESS); + TESTASSERT(pcap_handle->open("127.0.0.1") != SRSLTE_SUCCESS); // open again will fail + + std::vector writer_threads; + + for (uint32_t i = 0; i < num_threads; i++) { + writer_threads.push_back(std::thread(write_pcap_eutra_thread_function, pcap_handle.get(), tv, num_pdus_per_thread)); + } + + // wait for threads to finish + for (std::thread& thread : writer_threads) { + thread.join(); + } + TESTASSERT(pcap_handle->close() == SRSLTE_SUCCESS); + TESTASSERT(pcap_handle->close() != SRSLTE_SUCCESS); // closing twice will fail + + return SRSLTE_SUCCESS; +} + +int nr_mac_pcap_net_test() +{ + std::array tv = {0x42, 0x00, 0x08, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + + uint32_t num_threads = 10; + uint32_t num_pdus_per_thread = 100; + + std::unique_ptr pcap_handle = std::unique_ptr(new srslte::mac_pcap_net()); + TESTASSERT(pcap_handle->open("127.0.0.1") == SRSLTE_SUCCESS); + TESTASSERT(pcap_handle->open("127.0.0.1") != SRSLTE_SUCCESS); // open again will fail + + std::vector writer_threads; + + for (uint32_t i = 0; i < num_threads; i++) { + writer_threads.push_back(std::thread(write_pcap_nr_thread_function, pcap_handle.get(), tv, num_pdus_per_thread)); + } + + // wait for threads to finish + for (std::thread& thread : writer_threads) { + thread.join(); + } + + TESTASSERT(pcap_handle->close() == SRSLTE_SUCCESS); + TESTASSERT(pcap_handle->close() != SRSLTE_SUCCESS); // closing twice will fail + + return SRSLTE_SUCCESS; +} + +int main(int argc, char** argv) +{ + auto& mac_logger = srslog::fetch_basic_logger("MAC", false); + mac_logger.set_level(srslog::basic_levels::debug); + mac_logger.set_hex_dump_max_size(-1); + srslog::init(); + + TESTASSERT(lte_mac_pcap_net_test() == SRSLTE_SUCCESS); + TESTASSERT(nr_mac_pcap_net_test() == SRSLTE_SUCCESS); +} \ No newline at end of file