diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index 1ef303105..4931e9dbc 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -138,10 +138,14 @@ bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti) srsran::bounded_vector to_remove; for (lcid_tunnel& bearer : new_rnti_obj) { tunnels[bearer.teid].rnti = new_rnti; + // Remove forwarding path if (tunnels[bearer.teid].state == tunnel_state::forward_to) { - // Remove forwarding path tunnels[bearer.teid].state = tunnel_state::pdcp_active; tunnels[bearer.teid].fwd_tunnel = nullptr; + logger.info("Taking down forwarding tunnel for rnti=0x%x, lcid=%d. New default " TEID_IN_FMT, + new_rnti, + bearer.lcid, + bearer.teid); } else if (tunnels[bearer.teid].state == tunnel_state::forwarded_from) { to_remove.push_back(bearer.teid); } @@ -537,10 +541,19 @@ void gtpu::rem_tunnel(uint32_t teidin) void gtpu::rem_user(uint16_t rnti) { - if (tunnels.find_rnti_tunnels(rnti) == nullptr) { + const auto* tun_lst = tunnels.find_rnti_tunnels(rnti); + if (tun_lst == nullptr) { logger.info("Removing user - rnti=0x%x not found.", rnti); return; } + for (gtpu_tunnel_manager::lcid_tunnel tun_elem : *tun_lst) { + const gtpu_tunnel* tun = tunnels.find_tunnel(tun_elem.teid); + if (tun != nullptr and tun->state == gtpu_tunnel_manager::tunnel_state::forwarded_from) { + // In case of forwarding tunnel tx endpoint, send one extra End Marker on removal + send_end_marker(tun->teid_in); + rem_tunnel(tun->teid_in); + } + } tunnels.remove_rnti(rnti); } @@ -553,6 +566,7 @@ void gtpu::handle_end_marker(const gtpu_tunnel& rx_tunnel) // TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover // END MARKER should be forwarded to TeNB if forwarding is activated send_end_marker(rx_tunnel.fwd_tunnel->teid_in); + rem_tunnel(rx_tunnel.fwd_tunnel->teid_in); } // Remove tunnel that received End Marker diff --git a/srsenb/test/upper/gtpu_test.cc b/srsenb/test/upper/gtpu_test.cc index 020667ddd..2590f1ae1 100644 --- a/srsenb/test/upper/gtpu_test.cc +++ b/srsenb/test/upper/gtpu_test.cc @@ -205,7 +205,7 @@ void test_gtpu_tunnel_manager() TESTASSERT(after_tun->state == gtpu_tunnel_manager::tunnel_state::pdcp_active); } -enum class tunnel_test_event { success, wait_end_marker_timeout }; +enum class tunnel_test_event { success, wait_end_marker_timeout, ue_removal_no_marker, reest_senb }; int test_gtpu_direct_tunneling(tunnel_test_event event) { @@ -330,6 +330,25 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) for (size_t i = 0; i < gtpu_args.indirect_tunnel_timeout_msec + 1; ++i) { task_sched.tic(); } + } else if (event == tunnel_test_event::ue_removal_no_marker) { + // TEST: EndMarker may even reach SeNB, but the SeNB receives in tandem the UEContextReleaseCommand and closes + // the user tunnels before the chance to send an EndMarker + senb_gtpu.rem_user(0x46); + tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_rx_sockets.s1u_fd), senb_sockaddr); + } else if (event == tunnel_test_event::reest_senb) { + // TEST: UE may start a Reestablishment to the SeNB. In such case, the rnti will be updated, the forwarding tunnel + // taken down, and the previous main tunnel reestablished + senb_gtpu.mod_bearer_rnti(0x46, 0x47); + std::iota(data_vec.begin(), data_vec.end(), 0); + std::shuffle(data_vec.begin(), data_vec.end(), g); + pdu = encode_gtpu_packet(data_vec, senb_teid_in, sgw_sockaddr, senb_sockaddr); + encoded_data.assign(pdu->msg + 8u, pdu->msg + pdu->N_bytes); + senb_pdcp.last_sdu = nullptr; + senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr); + TESTASSERT(senb_pdcp.last_sdu != nullptr); + TESTASSERT(senb_pdcp.last_sdu->N_bytes == encoded_data.size() and + memcmp(senb_pdcp.last_sdu->msg, encoded_data.data(), encoded_data.size()) == 0); + return SRSRAN_SUCCESS; } else { // TEST: EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed pdu = encode_end_marker(senb_teid_in); @@ -357,6 +376,8 @@ int main(int argc, char** argv) srsenb::test_gtpu_tunnel_manager(); TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::success) == SRSRAN_SUCCESS); TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::wait_end_marker_timeout) == SRSRAN_SUCCESS); + TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::ue_removal_no_marker) == SRSRAN_SUCCESS); + TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::reest_senb) == SRSRAN_SUCCESS); srslog::flush();