From f88943653ba3db4f1825ebd6ebee79c80d83e9bf Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Wed, 27 Jan 2021 20:45:03 +0100 Subject: [PATCH] mac: implement MAC RAR PDU unpacking for NR * add class for mac_rar_pdu_nr * extend test case --- lib/include/srslte/mac/mac_rar_pdu_nr.h | 104 ++++++++++++ lib/src/mac/CMakeLists.txt | 2 +- lib/src/mac/mac_rar_pdu_nr.cc | 214 ++++++++++++++++++++++++ lib/test/mac/CMakeLists.txt | 2 +- lib/test/mac/mac_pdu_nr_test.cc | 102 ++++++++++- 5 files changed, 414 insertions(+), 10 deletions(-) create mode 100644 lib/include/srslte/mac/mac_rar_pdu_nr.h create mode 100644 lib/src/mac/mac_rar_pdu_nr.cc diff --git a/lib/include/srslte/mac/mac_rar_pdu_nr.h b/lib/include/srslte/mac/mac_rar_pdu_nr.h new file mode 100644 index 000000000..ae3084ebb --- /dev/null +++ b/lib/include/srslte/mac/mac_rar_pdu_nr.h @@ -0,0 +1,104 @@ +/** + * + * \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_RAR_PDU_NR_H +#define SRSLTE_MAC_RAR_PDU_NR_H + +#include "srslte/common/common.h" +#include "srslte/config.h" +#include "srslte/srslog/srslog.h" +#include +#include +#include + +namespace srslte { + +class mac_rar_pdu_nr; + +// 3GPP 38.321 v15.3.0 Sec 6.1.5 +class mac_rar_subpdu_nr +{ +public: + // Possible types of RAR subpdus (same like EUTRA) + typedef enum { BACKOFF = 0, RAPID } rar_subh_type_t; + + mac_rar_subpdu_nr(mac_rar_pdu_nr* parent_); + + // RAR content length in bits (38.321 Sec 6.2.3) + static const uint32_t UL_GRANT_NBITS = 27; + static const uint32_t TA_COMMAND_NBITS = 12; + + // getter + bool read_subpdu(const uint8_t* ptr); + bool has_more_subpdus(); + uint32_t get_total_length(); + bool has_rapid(); + uint8_t get_rapid(); + uint16_t get_temp_crnti(); + uint32_t get_ta(); + void get_ul_grant(std::array& grant); + + bool has_backoff(); + uint8_t get_backoff(); + + // setter + uint32_t write_subpdu(const uint8_t* start_); + void set_backoff(const uint8_t backoff_indicator_); + + std::string to_string(); + +private: + int header_length = 1; // RAR PDU subheader is always 1 B + int payload_length = 0; // only used if MAC RAR is included + + std::array ul_grant = {}; + uint16_t ta = 0; // 12bit TA + uint16_t temp_crnti = 0; + uint16_t rapid = 0; + uint8_t backoff_indicator = 0; + rar_subh_type_t type = BACKOFF; + bool E_bit = 0; + + srslog::basic_logger& logger; + + mac_rar_pdu_nr* parent = nullptr; +}; + +class mac_rar_pdu_nr +{ +public: + mac_rar_pdu_nr(); + ~mac_rar_pdu_nr() = default; + + bool pack(); + bool unpack(const uint8_t* payload, const uint32_t& len); + uint32_t get_num_subpdus(); + const mac_rar_subpdu_nr& get_subpdu(const uint32_t& index); + + uint32_t get_remaining_len(); + + void set_si_rapid(uint16_t si_rapid_); // configured through SIB1 for on-demand SI request (See 38.331 Sec 5.2.1) + bool has_si_rapid(); + + std::string to_string(); + +private: + std::vector subpdus; + uint32_t remaining_len = 0; + uint16_t si_rapid = 0; + bool si_rapid_set = false; + srslog::basic_logger& logger; +}; + +} // namespace srslte + +#endif // SRSLTE_MAC_RAR_PDU_NR_H diff --git a/lib/src/mac/CMakeLists.txt b/lib/src/mac/CMakeLists.txt index d4a156c7e..f2dd03f0b 100644 --- a/lib/src/mac/CMakeLists.txt +++ b/lib/src/mac/CMakeLists.txt @@ -9,7 +9,7 @@ SET(SOURCES pdu.cc pdu_queue.cc) if (ENABLE_5GNR) - set(SOURCES ${SOURCES} mac_sch_pdu_nr.cc) + set(SOURCES ${SOURCES} mac_sch_pdu_nr.cc mac_rar_pdu_nr.cc) endif(ENABLE_5GNR) add_library(srslte_mac STATIC ${SOURCES}) diff --git a/lib/src/mac/mac_rar_pdu_nr.cc b/lib/src/mac/mac_rar_pdu_nr.cc new file mode 100644 index 000000000..b4cf347cd --- /dev/null +++ b/lib/src/mac/mac_rar_pdu_nr.cc @@ -0,0 +1,214 @@ +/** + * + * \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/mac/mac_rar_pdu_nr.h" +#include +#ifdef __cplusplus +extern "C" { +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" +} +#endif + +namespace srslte { + +mac_rar_subpdu_nr::mac_rar_subpdu_nr(mac_rar_pdu_nr* parent_) : + parent(parent_), logger(srslog::fetch_basic_logger("MAC")) +{} + +// Return true if subPDU could be parsed correctly, false otherwise +bool mac_rar_subpdu_nr::read_subpdu(const uint8_t* ptr) +{ + E_bit = (bool)(*ptr & 0x80) ? true : false; + type = (*ptr & 0x40) ? RAPID : BACKOFF; + if (type == RAPID) { + rapid = *ptr & 0x3f; + // if PDU is not configured with SI request, extract MAC RAR + if (parent->has_si_rapid() == false) { + const uint32_t MAC_RAR_NBYTES = 7; + if (parent->get_remaining_len() >= MAC_RAR_NBYTES) { + uint8_t* rar = const_cast(ptr + 1); + // check reserved bits + if (*rar & 0x80) { + logger.error("Error parsing RAR PDU, reserved bit is set."); + return false; + } + // TA is first 7 bits of 1st + 5 bits of 2nd octet + ta = (uint16_t)(((*(rar + 0) & 0x7f) << 5 | ((*(rar + 1) & 0xf8) >> 3))); + // Extract the first 3 bits of the UL grant from the 2nd octet + ul_grant.at(0) = *(rar + 1) & 0x4 ? 1 : 0; + ul_grant.at(1) = *(rar + 1) & 0x2 ? 1 : 0; + ul_grant.at(2) = *(rar + 1) & 0x1 ? 1 : 0; + // And now the remaining 3 full octets + uint8_t* x = &ul_grant.at(3); + srslte_bit_unpack(*(rar + 2), &x, 8); + srslte_bit_unpack(*(rar + 3), &x, 8); + srslte_bit_unpack(*(rar + 4), &x, 8); + // Temp CRNTI is octet 6 + 7 + temp_crnti = ((uint16_t) * (rar + 5)) << 8 | *(rar + 6); + payload_length = MAC_RAR_NBYTES; + } else { + logger.error("Error parsing RAR PDU, remaining bytes not sufficant (%d < %d)", + parent->get_remaining_len(), + MAC_RAR_NBYTES); + return false; + } + } + } else { + // check reserved bits + if (*ptr & 0x10 || *ptr & 0x20) { + logger.error("Error parsing RAR PDU, reserved bit is set."); + return false; + } + backoff_indicator = *ptr & 0xf; + } + return true; +} + +// Return true if another subPDU follows after that +bool mac_rar_subpdu_nr::has_more_subpdus() +{ + return E_bit; +} + +// Section 6.1.2 +uint32_t mac_rar_subpdu_nr::write_subpdu(const uint8_t* start_) +{ + return 0; +} + +uint32_t mac_rar_subpdu_nr::get_total_length() +{ + return (header_length + payload_length); +} + +bool mac_rar_subpdu_nr::has_rapid() +{ + return (type == rar_subh_type_t::RAPID); +} + +uint8_t mac_rar_subpdu_nr::get_rapid() +{ + return rapid; +} + +uint32_t mac_rar_subpdu_nr::get_ta() +{ + return ta; +} + +uint16_t mac_rar_subpdu_nr::get_temp_crnti() +{ + return temp_crnti; +} + +bool mac_rar_subpdu_nr::has_backoff() +{ + return (type == rar_subh_type_t::BACKOFF); +} + +void mac_rar_subpdu_nr::set_backoff(const uint8_t backoff_indicator_) +{ + backoff_indicator = backoff_indicator_; +} + +void mac_rar_subpdu_nr::get_ul_grant(std::array& grant_) +{ + grant_ = ul_grant; +} + +std::string mac_rar_subpdu_nr::to_string() +{ + std::stringstream ss; + if (has_rapid()) { + ss << "RAPID: " << rapid << ", Temp C-RNTI: " << temp_crnti << ", TA: " << ta << ", UL Grant: "; + } else { + ss << "Backoff Indicator: " << backoff_indicator << " "; + } + + char tmp[16] = {}; + srslte_vec_sprint_hex(tmp, sizeof(tmp), ul_grant.data(), UL_GRANT_NBITS); + ss << tmp; + + return ss.str(); +} + +mac_rar_pdu_nr::mac_rar_pdu_nr() : logger(srslog::fetch_basic_logger("MAC")) {} + +bool mac_rar_pdu_nr::pack() +{ + // not implemented yet + return false; +} + +bool mac_rar_pdu_nr::has_si_rapid() +{ + return si_rapid_set; +} + +void mac_rar_pdu_nr::set_si_rapid(uint16_t si_rapid_) +{ + si_rapid = si_rapid_; + si_rapid_set = true; +} + +// Return true if PDU could be parsed successfully +bool mac_rar_pdu_nr::unpack(const uint8_t* payload, const uint32_t& len) +{ + bool ret = false; + bool have_more_subpdus = false; + uint32_t offset = 0; + + remaining_len = len; + + do { + mac_rar_subpdu_nr rar_subpdu(this); + ret = rar_subpdu.read_subpdu(payload + offset); + have_more_subpdus = rar_subpdu.has_more_subpdus(); + offset += rar_subpdu.get_total_length(); + remaining_len -= rar_subpdu.get_total_length(); + + // only append if subPDU could be read successfully + if (ret == true) { + subpdus.push_back(rar_subpdu); + } + // continue reading as long as subPDUs can be extracted ok and we are not overrunning the PDU length + } while (ret && have_more_subpdus && offset <= len); + + return ret; +} + +uint32_t mac_rar_pdu_nr::get_num_subpdus() +{ + return subpdus.size(); +} + +const mac_rar_subpdu_nr& mac_rar_pdu_nr::get_subpdu(const uint32_t& index) +{ + return subpdus.at(index); +} + +uint32_t mac_rar_pdu_nr::get_remaining_len() +{ + return remaining_len; +} + +std::string mac_rar_pdu_nr::to_string() +{ + std::stringstream ss; + for (auto& subpdu : subpdus) { + ss << subpdu.to_string() << " "; + } + return ss.str(); +} + +} // namespace srslte diff --git a/lib/test/mac/CMakeLists.txt b/lib/test/mac/CMakeLists.txt index b8ba83de7..9321a971c 100644 --- a/lib/test/mac/CMakeLists.txt +++ b/lib/test/mac/CMakeLists.txt @@ -16,6 +16,6 @@ add_test(mac_pcap_test mac_pcap_test) if (ENABLE_5GNR) add_executable(mac_pdu_nr_test mac_pdu_nr_test.cc) - target_link_libraries(mac_pdu_nr_test srslte_phy srslte_mac srslte_common ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(mac_pdu_nr_test srslte_mac srslte_common ${CMAKE_THREAD_LIBS_INIT}) add_test(mac_pdu_nr_test mac_pdu_nr_test) endif (ENABLE_5GNR) diff --git a/lib/test/mac/mac_pdu_nr_test.cc b/lib/test/mac/mac_pdu_nr_test.cc index 6d1b736b4..677215b24 100644 --- a/lib/test/mac/mac_pdu_nr_test.cc +++ b/lib/test/mac/mac_pdu_nr_test.cc @@ -12,7 +12,9 @@ #include "srslte/common/log_filter.h" #include "srslte/common/mac_nr_pcap.h" +#include "srslte/common/test_common.h" #include "srslte/config.h" +#include "srslte/mac/mac_rar_pdu_nr.h" #include "srslte/mac/mac_sch_pdu_nr.h" #include @@ -20,14 +22,6 @@ #include #include -#define TESTASSERT(cond) \ - { \ - if (!(cond)) { \ - std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ - return -1; \ - } \ - } - #define PCAP 0 #define PCAP_CRNTI (0x1001) #define PCAP_TTI (666) @@ -260,6 +254,88 @@ int mac_dl_sch_pdu_unpack_test6() return SRSLTE_SUCCESS; } +int mac_rar_pdu_unpack_test7() +{ + // MAC PDU with RAR PDU with single RAPID=0 + // rapid=0 + // ta=180 + // ul_grant: + // hopping_flag=0 + // riv=0x1 + // time_domain_rsc=1 + // mcs=4 + // tpc_command=3 + // csi_request=0 + // tc-rnti=0x4616 + + // Bit 1-8 + // | | | | | | | | | + // | R |T=1| RAPID=0 | Octet 1 + // | RAR | Octet 2-8 + const uint32_t tv_rapid = 0; + const uint32_t tv_ta = 180; + const uint16_t tv_tcrnti = 0x4616; + const uint8_t tv_msg3_grant[mac_rar_subpdu_nr::UL_GRANT_NBITS] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}; // unpacked UL grant + + uint8_t mac_dl_rar_pdu[] = {0x40, 0x05, 0xa0, 0x00, 0x11, 0x46, 0x46, 0x16, 0x00, 0x00, 0x00}; + + if (pcap_handle) { + pcap_handle->write_dl_ra_rnti(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu), 0x0016, true, PCAP_TTI); + } + + srslte::mac_rar_pdu_nr pdu; + TESTASSERT(pdu.unpack(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu)) == true); + + std::cout << pdu.to_string() << std::endl; + + TESTASSERT(pdu.get_num_subpdus() == 1); + + mac_rar_subpdu_nr subpdu = pdu.get_subpdu(0); + TESTASSERT(subpdu.has_rapid() == true); + TESTASSERT(subpdu.has_backoff() == false); + TESTASSERT(subpdu.get_temp_crnti() == tv_tcrnti); + TESTASSERT(subpdu.get_ta() == tv_ta); + TESTASSERT(subpdu.get_rapid() == tv_rapid); + + std::array msg3_grant; + subpdu.get_ul_grant(msg3_grant); + TESTASSERT(memcmp(msg3_grant.data(), tv_msg3_grant, msg3_grant.size()) == 0); + + return SRSLTE_SUCCESS; +} + +int mac_rar_pdu_unpack_test8() +{ + // Malformed MAC PDU, says it has RAR PDU but is too short to include MAC RAR + + // Bit 1-8 + // | | | | | | | | | + // | E |T=1| RAPID=0 | Octet 1 + // | RAR_fragment | Octet 2 + uint8_t mac_dl_rar_pdu[] = {0x40, 0x05}; + + if (pcap_handle) { + pcap_handle->write_dl_ra_rnti(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu), 0x0016, true, PCAP_TTI); + } + + // unpacking should fail + srslte::mac_rar_pdu_nr pdu; + TESTASSERT(pdu.unpack(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu)) == false); + TESTASSERT(pdu.get_num_subpdus() == 0); + + // Malformed PDU with reserved bits set + // Bit 1-8 + // | | | | | | | | | + // | E |T=0| R | R | BI | Octet 1 + uint8_t mac_dl_rar_pdu2[] = {0x10}; + TESTASSERT(pdu.unpack(mac_dl_rar_pdu2, sizeof(mac_dl_rar_pdu2)) == false); + TESTASSERT(pdu.get_num_subpdus() == 0); + + return SRSLTE_SUCCESS; +} + int mac_ul_sch_pdu_unpack_test1() { // UL-SCH MAC PDU with fixed-size CE and DL-SCH subheader with 16-bit length field @@ -498,6 +574,16 @@ int main(int argc, char** argv) return SRSLTE_ERROR; } + if (mac_rar_pdu_unpack_test7()) { + fprintf(stderr, "mac_rar_pdu_unpack_test7() failed.\n"); + return SRSLTE_ERROR; + } + + if (mac_rar_pdu_unpack_test8()) { + fprintf(stderr, "mac_rar_pdu_unpack_test8() failed.\n"); + return SRSLTE_ERROR; + } + if (mac_ul_sch_pdu_unpack_test1()) { fprintf(stderr, "mac_ul_sch_pdu_unpack_test1() failed.\n"); return SRSLTE_ERROR;