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
This commit is contained in:
Andre Puschmann 2020-04-08 14:43:24 +02:00
parent 66a799661e
commit d35c9e2b89
7 changed files with 138 additions and 6 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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
*******************************************************************************/

View File

@ -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<LIBLTE_MME_UE_TEST_LOOP_MODE_ENUM>(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();
}

View File

@ -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<uint32_t, block_queue<srslte::unique_byte_buffer_t> > pdu_queue; // A PDU queue for each DRB
tft_pdu_matcher tft_matcher;
all_args_t args = {};
};

View File

@ -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