From d35c9e2b8998c92508f3abcf218e23f904908917 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Wed, 8 Apr 2020 14:43:24 +0200 Subject: [PATCH] nas: add support ot UE test loop mode B extend GW-NAS interface to signal test mode activation. The method is a noop in the normal GW but is implemented in the TTCN3 DUT according to TS 36.509 for Mode B --- lib/include/srslte/asn1/liblte_mme.h | 11 +++ lib/include/srslte/interfaces/ue_interfaces.h | 14 +++ srsue/hdr/stack/upper/gw.h | 1 + srsue/src/stack/upper/gw.cc | 5 + srsue/src/stack/upper/nas.cc | 20 +++- srsue/test/ttcn3/hdr/ttcn3_ue.h | 92 ++++++++++++++++++- srsue/test/upper/nas_test.cc | 1 + 7 files changed, 138 insertions(+), 6 deletions(-) diff --git a/lib/include/srslte/asn1/liblte_mme.h b/lib/include/srslte/asn1/liblte_mme.h index 35af99f4c..8729737aa 100644 --- a/lib/include/srslte/asn1/liblte_mme.h +++ b/lib/include/srslte/asn1/liblte_mme.h @@ -4007,6 +4007,17 @@ liblte_mme_unpack_pdn_disconnect_request_msg(LIBLTE_BYTE_MSG_STRUCT* LIBLTE_ERROR_ENUM liblte_mme_pack_activate_test_mode_complete_msg(LIBLTE_BYTE_MSG_STRUCT* msg, uint8 sec_hdr_type, uint32 count); +// UE test mode type +typedef enum { + LIBLTE_MME_UE_TEST_LOOP_MODE_A = 0, + LIBLTE_MME_UE_TEST_LOOP_MODE_B, + LIBLTE_MME_UE_TEST_LOOP_MODE_C, + LIBLTE_MME_UE_TEST_LOOP_MODE_N_ITEMS, +} LIBLTE_MME_UE_TEST_LOOP_MODE_ENUM; +static const char liblte_ue_test_loop_mode_text[LIBLTE_MME_UE_TEST_LOOP_MODE_N_ITEMS][20] = {"UE test loop mode A", + "UE test loop mode B", + "UE test loop mode C"}; + /********************************************************************* Message Name: CLOSE UE TEST LOOP COMPLETE diff --git a/lib/include/srslte/interfaces/ue_interfaces.h b/lib/include/srslte/interfaces/ue_interfaces.h index 3b11d6862..2a3af47da 100644 --- a/lib/include/srslte/interfaces/ue_interfaces.h +++ b/lib/include/srslte/interfaces/ue_interfaces.h @@ -89,6 +89,20 @@ public: virtual int apply_traffic_flow_template(const uint8_t& eps_bearer_id, const uint8_t& lcid, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) = 0; + + typedef enum { + TEST_LOOP_INACTIVE = 0, + TEST_LOOP_MODE_A_ACTIVE, + TEST_LOOP_MODE_B_ACTIVE, + TEST_LOOP_MODE_C_ACTIVE + } test_loop_mode_state_t; + + /** + * Updates the test loop mode. The IP delay parameter is only valid for Mode B. + * @param mode + * @param ip_pdu_delay_ms The PDU delay in ms + */ + virtual void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms = 0) = 0; }; // GW interface for RRC diff --git a/srsue/hdr/stack/upper/gw.h b/srsue/hdr/stack/upper/gw.h index 651d0d80d..eb3e89760 100644 --- a/srsue/hdr/stack/upper/gw.h +++ b/srsue/hdr/stack/upper/gw.h @@ -63,6 +63,7 @@ public: int apply_traffic_flow_template(const uint8_t& eps_bearer_id, const uint8_t& lcid, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft); + void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms); // RRC interface void add_mch_port(uint32_t lcid, uint32_t port); diff --git a/srsue/src/stack/upper/gw.cc b/srsue/src/stack/upper/gw.cc index d77b4afae..27b51390c 100644 --- a/srsue/src/stack/upper/gw.cc +++ b/srsue/src/stack/upper/gw.cc @@ -192,6 +192,11 @@ int gw::apply_traffic_flow_template(const uint8_t& return tft_matcher.apply_traffic_flow_template(erab_id, lcid, tft); } +void gw::set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms) +{ + log.error("UE test loop mode not supported\n"); +} + /******************************************************************************* RRC interface *******************************************************************************/ diff --git a/srsue/src/stack/upper/nas.cc b/srsue/src/stack/upper/nas.cc index 7df7d2cb6..11191b0d7 100644 --- a/srsue/src/stack/upper/nas.cc +++ b/srsue/src/stack/upper/nas.cc @@ -700,6 +700,8 @@ void nas::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) // TODO: Handle deactivate test mode and ue open test loop case LIBLTE_MME_MSG_TYPE_OPEN_UE_TEST_LOOP: case LIBLTE_MME_MSG_TYPE_DEACTIVATE_TEST_MODE: + gw->set_test_loop_mode(gw_interface_nas::TEST_LOOP_INACTIVE); + break; default: nas_log->error("Not handling NAS message with MSG_TYPE=%02X\n", msg_type); return; @@ -1678,12 +1680,24 @@ void nas::parse_activate_test_mode(uint32_t lcid, unique_byte_buffer_t pdu) void nas::parse_close_ue_test_loop(uint32_t lcid, unique_byte_buffer_t pdu) { - nas_log->info("Received Close UE test loop\n"); + LIBLTE_MME_UE_TEST_LOOP_MODE_ENUM mode = static_cast(pdu->msg[8]); + nas_log->info("Received Close UE test loop for %s\n", liblte_ue_test_loop_mode_text[mode]); + switch (mode) { + case LIBLTE_MME_UE_TEST_LOOP_MODE_A: + gw->set_test_loop_mode(gw_interface_nas::TEST_LOOP_MODE_A_ACTIVE); + break; + case LIBLTE_MME_UE_TEST_LOOP_MODE_B: + gw->set_test_loop_mode(gw_interface_nas::TEST_LOOP_MODE_B_ACTIVE, pdu->msg[9] * 1000); + break; + case LIBLTE_MME_UE_TEST_LOOP_MODE_C: + gw->set_test_loop_mode(gw_interface_nas::TEST_LOOP_MODE_C_ACTIVE); + break; + default: + break; + } ctxt.rx_count++; - // TODO: Save the test loop mode - send_close_ue_test_loop_complete(); } diff --git a/srsue/test/ttcn3/hdr/ttcn3_ue.h b/srsue/test/ttcn3/hdr/ttcn3_ue.h index 83fbc63a6..24f75dae9 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_ue.h +++ b/srsue/test/ttcn3/hdr/ttcn3_ue.h @@ -29,7 +29,7 @@ class ttcn3_ue : public phy_interface_syssim, public gw_interface_stack { public: - ttcn3_ue() {} + ttcn3_ue() : tft_matcher(&log) {} virtual ~ttcn3_ue() {} @@ -133,14 +133,95 @@ public: // GW interface void add_mch_port(uint32_t lcid, uint32_t port) {} - void write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) {} + void write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) + { + log.debug_hex(pdu->msg, pdu->N_bytes, "Rx PDU (%d B) on lcid=%d\n", pdu->N_bytes, lcid); + switch (test_loop_mode) { + case TEST_LOOP_INACTIVE: + log.warning("Test loop inactive. Dropping PDU.\n"); + break; + case TEST_LOOP_MODE_A_ACTIVE: + log.error("Test loop mode A not implemented. Dropping PDU.\n"); + break; + case TEST_LOOP_MODE_B_ACTIVE: + // Section 5.4.4 in TS 36.509 + if (pdu_delay_timer.is_running()) { + pdu_queue[lcid].push(std::move(pdu)); + } else { + if (pdu_delay_timer.is_valid()) { + pdu_queue[lcid].push(std::move(pdu)); + pdu_delay_timer.run(); // timer is already set + } else { + loop_back_pdu_with_tft(lcid, std::move(pdu)); + } + } + break; + case TEST_LOOP_MODE_C_ACTIVE: + log.error("Test loop mode C not implemented. Dropping PDU.\n"); + break; + } + } void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu) {} int setup_if_addr(uint32_t lcid, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str) { return 0; } + int apply_traffic_flow_template(const uint8_t& eps_bearer_id, const uint8_t& lcid, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) { - return 0; + return tft_matcher.apply_traffic_flow_template(eps_bearer_id, lcid, tft); + } + + void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms_ = 0) + { + test_loop_mode = mode; + switch (test_loop_mode) { + case TEST_LOOP_INACTIVE: + // deactivate timer + log.info("Deactivating Test Loop Mode\n"); + pdu_delay_timer.release(); + break; + case TEST_LOOP_MODE_A_ACTIVE: + log.error("Test loop mode A not implemented\n"); + break; + case TEST_LOOP_MODE_B_ACTIVE: + log.info("Activating Test loop mode B with %d ms PDU delay\n", ip_pdu_delay_ms_); + // only create timer if needed + if (ip_pdu_delay_ms_ > 0) { + pdu_delay_timer = stack->get_unique_timer(); + pdu_delay_timer.set(ip_pdu_delay_ms_, [this](uint32_t tid) { timer_expired(tid); }); + } + break; + case TEST_LOOP_MODE_C_ACTIVE: + log.error("Test loop mode A not implemented\n"); + break; + } + } + + void timer_expired(uint32_t timeout_id) + { + if (timeout_id == pdu_delay_timer.id()) { + log.info("Testmode B PDU delay timer expired\n"); + for (auto& bearer_pdu_queue : pdu_queue) { + log.info("Delivering %zd buffered PDUs for LCID=%d\n", bearer_pdu_queue.second.size(), bearer_pdu_queue.first); + while (not pdu_queue.empty()) { + srslte::unique_byte_buffer_t pdu; + bearer_pdu_queue.second.try_pop(&pdu); + loop_back_pdu_with_tft(bearer_pdu_queue.first, std::move(pdu)); + } + } + } + } + + void loop_back_pdu_with_tft(uint32_t input_lcid, srslte::unique_byte_buffer_t pdu) + { + uint8_t output_lcid = tft_matcher.check_tft_filter_match(pdu); + log.info_hex(pdu->msg, + pdu->N_bytes, + "Rx PDU (%d B) on lcid=%d, looping back to lcid=%d\n", + pdu->N_bytes, + input_lcid, + output_lcid); + stack->write_sdu(input_lcid, std::move(pdu), false); } private: @@ -151,6 +232,11 @@ private: srslte::logger* logger = nullptr; srslte::log_filter log; // Own logger for UE + test_loop_mode_state_t test_loop_mode = TEST_LOOP_INACTIVE; + srslte::timer_handler::unique_timer pdu_delay_timer; + std::map > pdu_queue; // A PDU queue for each DRB + tft_pdu_matcher tft_matcher; + all_args_t args = {}; }; diff --git a/srsue/test/upper/nas_test.cc b/srsue/test/upper/nas_test.cc index 48b52f277..a9665cd43 100644 --- a/srsue/test/upper/nas_test.cc +++ b/srsue/test/upper/nas_test.cc @@ -196,6 +196,7 @@ class gw_dummy : public gw_interface_nas, public gw_interface_pdcp } void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) {} void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t sdu) {} + void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms = 0) {} }; } // namespace srslte