From 3cf5e85b20db8c9e69c3a41d2769b580297f7bca Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 24 Mar 2021 10:03:00 +0000 Subject: [PATCH] create separate class to handle gtpu tunnel creation/deletion/search --- srsenb/hdr/stack/upper/common_enb.h | 1 + srsenb/hdr/stack/upper/gtpu.h | 74 +++-- srsenb/src/stack/upper/gtpu.cc | 407 ++++++++++++++++------------ srsenb/test/upper/gtpu_test.cc | 30 +- 4 files changed, 307 insertions(+), 205 deletions(-) diff --git a/srsenb/hdr/stack/upper/common_enb.h b/srsenb/hdr/stack/upper/common_enb.h index e0b0a0efa..98d1a8c02 100644 --- a/srsenb/hdr/stack/upper/common_enb.h +++ b/srsenb/hdr/stack/upper/common_enb.h @@ -26,6 +26,7 @@ namespace srsenb { #define SRSENB_N_SRB 3 #define SRSENB_N_DRB 8 #define SRSENB_N_RADIO_BEARERS 11 +#define SRSENB_MAX_UES 64 enum rb_id_t { RB_ID_SRB0 = 0, diff --git a/srsenb/hdr/stack/upper/gtpu.h b/srsenb/hdr/stack/upper/gtpu.h index b587de906..4b7e1f23f 100644 --- a/srsenb/hdr/stack/upper/gtpu.h +++ b/srsenb/hdr/stack/upper/gtpu.h @@ -15,6 +15,7 @@ #include #include "common_enb.h" +#include "srsran/adt/circular_map.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/task_scheduler.h" #include "srsran/common/threads.h" @@ -36,10 +37,52 @@ namespace srsenb { class pdcp_interface_gtpu; class stack_interface_gtpu_lte; +struct gtpu_tunnel { + bool dl_enabled = true; + bool fwd_teid_in_present = false; + bool prior_teid_in_present = false; + uint16_t rnti = SRSRAN_INVALID_RNTI; + uint32_t lcid = SRSENB_N_RADIO_BEARERS; + uint32_t teid_in = 0; + uint32_t teid_out = 0; + uint32_t spgw_addr = 0; + uint32_t fwd_teid_in = 0; ///< forward Rx SDUs to this TEID + uint32_t prior_teid_in = 0; ///< buffer bearer SDUs until this TEID receives an End Marker + srsran::unique_timer rx_timer; + std::vector > buffer; +}; + +class gtpu_tunnel_manager +{ +public: + using lcid_teid_list = std::vector; + using ue_lcid_tunnel_list = srsran::static_circular_map; + + gtpu_tunnel_manager(); + + gtpu_tunnel* find_tunnel(uint32_t teid); + ue_lcid_tunnel_list* find_rnti_tunnels(uint16_t rnti); + srsran::span find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid); + + gtpu_tunnel* add_tunnel(uint32_t teid, uint16_t rnti, uint32_t lcid); + bool update_rnti(uint16_t old_rnti, uint16_t new_rnti); + + bool remove_tunnel(uint32_t teid); + bool remove_bearer(uint16_t rnti, uint32_t lcid); + bool remove_rnti(uint16_t rnti); + +private: + srslog::basic_logger& logger; + + std::unordered_map tunnels; + srsran::static_circular_map ue_teidin_db; +}; + class gtpu final : public gtpu_interface_rrc, public gtpu_interface_pdcp { public: explicit gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger); + ~gtpu(); int init(std::string gtp_bind_addr_, std::string mme_addr_, @@ -108,23 +151,8 @@ private: }; m1u_handler m1u; - const uint32_t undefined_pdcp_sn = std::numeric_limits::max(); - struct tunnel { - bool dl_enabled = true; - bool fwd_teid_in_present = false; - bool prior_teid_in_present = false; - uint16_t rnti = SRSRAN_INVALID_RNTI; - uint32_t lcid = SRSENB_N_RADIO_BEARERS; - uint32_t teid_in = 0; - uint32_t teid_out = 0; - uint32_t spgw_addr = 0; - uint32_t fwd_teid_in = 0; ///< forward Rx SDUs to this TEID - uint32_t prior_teid_in = 0; ///< buffer bearer SDUs until this TEID receives an End Marker - srsran::unique_timer rx_timer; - std::multimap buffer; - }; - std::unordered_map tunnels; - std::map, SRSENB_N_RADIO_BEARERS> > ue_teidin_db; + const uint32_t undefined_pdcp_sn = std::numeric_limits::max(); + gtpu_tunnel_manager tunnels; // Tx sequence number for signaling messages uint32_t tx_seq = 0; @@ -132,14 +160,15 @@ private: // Socket file descriptor int fd = -1; - void send_pdu_to_tunnel(tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn = -1); + void send_pdu_to_tunnel(gtpu_tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn = -1); void echo_response(in_addr_t addr, in_port_t port, uint16_t seq); void error_indication(in_addr_t addr, in_port_t port, uint32_t err_teid); bool end_marker(uint32_t teidin); - void handle_end_marker(tunnel& rx_tunnel); - void handle_msg_data_pdu(const srsran::gtpu_header_t& header, tunnel& rx_tunnel, srsran::unique_byte_buffer_t pdu); + void handle_end_marker(gtpu_tunnel& rx_tunnel); + void + handle_msg_data_pdu(const srsran::gtpu_header_t& header, gtpu_tunnel& rx_tunnel, srsran::unique_byte_buffer_t pdu); int create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in); @@ -148,10 +177,7 @@ private: ***************************************************************************/ uint32_t next_teid_in = 0; - tunnel* get_tunnel(uint32_t teidin); - srsran::span get_lcid_teids(uint16_t rnti, uint32_t lcid); - - void log_message(tunnel& tun, bool is_rx, srsran::span pdu, int pdcp_sn = -1); + void log_message(const gtpu_tunnel& tun, bool is_rx, srsran::span pdu, int pdcp_sn = -1); }; } // namespace srsenb diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index f6c527d7d..7cbffc4f6 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -26,12 +26,147 @@ #include using namespace srsran; + namespace srsenb { +gtpu_tunnel_manager::gtpu_tunnel_manager() : logger(srslog::fetch_basic_logger("GTPU")) {} + +gtpu_tunnel* gtpu_tunnel_manager::find_tunnel(uint32_t teid) +{ + auto it = tunnels.find(teid); + return it != tunnels.end() ? &it->second : nullptr; +} + +gtpu_tunnel_manager::ue_lcid_tunnel_list* gtpu_tunnel_manager::find_rnti_tunnels(uint16_t rnti) +{ + if (not ue_teidin_db.contains(rnti)) { + return nullptr; + } + return &ue_teidin_db[rnti]; +} + +srsran::span gtpu_tunnel_manager::find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid) +{ + if (lcid < SRSENB_N_SRB or lcid >= SRSENB_N_RADIO_BEARERS) { + logger.warning("Searching for bearer with invalid lcid=%d", lcid); + return {}; + } + auto* ue_ptr = find_rnti_tunnels(rnti); + if (ue_ptr == nullptr or not ue_ptr->contains(lcid)) { + return {}; + } + return (*ue_ptr)[lcid]; +} + +gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint32_t teidin, uint16_t rnti, uint32_t lcid) +{ + auto ret_pair = tunnels.emplace(teidin, gtpu_tunnel()); + if (not ret_pair.second) { + logger.warning("Adding GTPU TEID In=0x%x", teidin); + return nullptr; + } + + if (not ue_teidin_db.contains(rnti)) { + ue_teidin_db.insert(rnti, ue_lcid_tunnel_list()); + } + if (not ue_teidin_db[rnti].contains(lcid)) { + ue_teidin_db[rnti].insert(lcid, std::vector()); + } + ue_teidin_db[rnti][lcid].push_back(teidin); + + return &ret_pair.first->second; +} + +bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti) +{ + auto* old_rnti_ptr = find_rnti_tunnels(old_rnti); + if (old_rnti_ptr == nullptr or find_rnti_tunnels(new_rnti) != nullptr) { + logger.error("Modifying bearer rnti. Old rnti=0x%x, new rnti=0x%x", old_rnti, new_rnti); + return false; + } + logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti); + + // Change RNTI bearers map + ue_teidin_db.insert(new_rnti, std::move(*old_rnti_ptr)); + ue_teidin_db.erase(old_rnti); + + // Change TEID in existing tunnels + auto* new_rnti_ptr = find_rnti_tunnels(new_rnti); + for (auto& bearer : *new_rnti_ptr) { + for (uint32_t teid : bearer.second) { + tunnels[teid].rnti = new_rnti; + } + } + + return true; +} + +bool gtpu_tunnel_manager::remove_tunnel(uint32_t teidin) +{ + auto it = tunnels.find(teidin); + if (it == tunnels.end()) { + logger.warning("Removing GTPU tunnel TEID In=0x%x", teidin); + return false; + } + gtpu_tunnel& tun = it->second; + srsran_assert(ue_teidin_db.contains(tun.rnti) or ue_teidin_db[tun.rnti].contains(tun.lcid), + "inconsistency in internal data structs"); + + std::vector& lcids = ue_teidin_db[tun.rnti][tun.lcid]; + lcids.erase(std::remove(lcids.begin(), lcids.end(), teidin), lcids.end()); + + logger.info("TEID In=%d for rnti=0x%x erased", teidin, tun.rnti); + tunnels.erase(it); + return true; +} + +bool gtpu_tunnel_manager::remove_bearer(uint16_t rnti, uint32_t lcid) +{ + ue_lcid_tunnel_list* ue_ptr = find_rnti_tunnels(rnti); + if (ue_ptr == nullptr or not ue_ptr->contains(lcid)) { + logger.warning("Removing rnti=0x%x, lcid=%d", rnti, lcid); + return false; + } + + logger.info("Removing rnti=0x%x,lcid=%d", rnti, lcid); + lcid_teid_list& lcid_list = (*ue_ptr)[lcid]; + for (uint32_t teid : lcid_list) { + srsran_expect(tunnels.erase(teid) > 0, "Inconsistency detected between two internal data structures"); + } + ue_ptr->erase(lcid); + return true; +} + +bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti) +{ + if (not ue_teidin_db.contains(rnti)) { + logger.warning("Removing rnti. rnti=0x%x not found.", rnti); + return false; + } + logger.info("Removing rnti=0x%x", rnti); + + for (auto& lcid_pair : ue_teidin_db[rnti]) { + for (uint32_t teid : lcid_pair.second) { + srsran_expect(tunnels.erase(teid) > 0, "Inconsistency detected between two internal data structures"); + } + } + ue_teidin_db.erase(rnti); + return true; +} + +/******************** + * GTPU class + *******************/ + gtpu::gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) : m1u(this), task_sched(task_sched_), logger(logger) {} +gtpu::~gtpu() +{ + stop(); +} + int gtpu::init(std::string gtp_bind_addr_, std::string mme_addr_, std::string m1u_multiaddr_, @@ -90,24 +225,26 @@ int gtpu::init(std::string gtp_bind_addr_, void gtpu::stop() { - if (fd) { + if (fd > 0) { close(fd); + fd = -1; } } // gtpu_interface_pdcp void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) { - srsran::span teids = get_lcid_teids(rnti, lcid); + srsran::span teids = tunnels.find_rnti_lcid_tunnels(rnti, lcid); if (teids.empty()) { + logger.warning("The rnti=0x%x,lcid=%d does not have any active tunnel", rnti, lcid); return; } - tunnel& tx_tun = tunnels[teids[0]]; + gtpu_tunnel& tx_tun = *tunnels.find_tunnel(teids[0]); log_message(tx_tun, false, srsran::make_span(pdu)); send_pdu_to_tunnel(tx_tun, std::move(pdu)); } -void gtpu::send_pdu_to_tunnel(tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn) +void gtpu::send_pdu_to_tunnel(gtpu_tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn) { // Check valid IP version struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; @@ -149,16 +286,16 @@ void gtpu::send_pdu_to_tunnel(tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, uint32_t gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props) { // Allocate a TEID for the incoming tunnel - uint32_t teid_in = ++next_teid_in; - auto insert_ret = tunnels.emplace(teid_in, tunnel{}); - tunnel& new_tun = insert_ret.first->second; - new_tun.teid_in = teid_in; - new_tun.rnti = rnti; - new_tun.lcid = lcid; - new_tun.spgw_addr = addr; - new_tun.teid_out = teid_out; - - ue_teidin_db[rnti][lcid].push_back(teid_in); + uint32_t teid_in = ++next_teid_in; + gtpu_tunnel* new_tun = tunnels.add_tunnel(teid_in, rnti, lcid); + if (new_tun == nullptr) { + return -1; + } + new_tun->teid_in = teid_in; + new_tun->rnti = rnti; + new_tun->lcid = lcid; + new_tun->spgw_addr = addr; + new_tun->teid_out = teid_out; fmt::memory_buffer str_buffer; srsran::gtpu_ntoa(str_buffer, htonl(addr)); @@ -172,23 +309,28 @@ uint32_t gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t if (props != nullptr) { if (props->flush_before_teidin_present) { // GTPU should wait for the bearer ctxt to arrive before sending SDUs from DL tunnel to PDCP - new_tun.dl_enabled = false; + new_tun->dl_enabled = false; // GTPU should not forward SDUs from main tunnel until the SeNB-TeNB tunnel has been flushed - tunnel& after_tun = tunnels.at(props->flush_before_teidin); - after_tun.dl_enabled = false; - after_tun.prior_teid_in_present = true; - after_tun.prior_teid_in = teid_in; + gtpu_tunnel* after_tun = tunnels.find_tunnel(props->flush_before_teidin); + if (after_tun == nullptr) { + logger.error("Setting priority relation between tunnels. TEID=0x%x not found", props->flush_before_teidin); + tunnels.remove_tunnel(teid_in); + return -1; + } + after_tun->dl_enabled = false; + after_tun->prior_teid_in_present = true; + after_tun->prior_teid_in = teid_in; + // Schedule autoremoval of this indirect tunnel - uint32_t after_teidin = after_tun.teid_in; - uint32_t before_teidin = new_tun.teid_in; - new_tun.rx_timer = task_sched.get_unique_timer(); - new_tun.rx_timer.set(500, [this, before_teidin, after_teidin](uint32_t tid) { - auto it = tunnels.find(after_teidin); - if (it != tunnels.end()) { - tunnel& after_tun = it->second; - if (after_tun.prior_teid_in_present) { - after_tun.prior_teid_in_present = false; - set_tunnel_status(after_tun.teid_in, true); + uint32_t after_teidin = after_tun->teid_in; + uint32_t before_teidin = new_tun->teid_in; + new_tun->rx_timer = task_sched.get_unique_timer(); + new_tun->rx_timer.set(500, [this, before_teidin, after_teidin](uint32_t tid) { + gtpu_tunnel* after_tun = tunnels.find_tunnel(after_teidin); + if (after_tun != nullptr) { + if (after_tun->prior_teid_in_present) { + after_tun->prior_teid_in_present = false; + set_tunnel_status(after_tun->teid_in, true); } // else: indirect tunnel already removed } else { @@ -197,7 +339,7 @@ uint32_t gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t // This will self-destruct the callback object rem_tunnel(before_teidin); }); - new_tun.rx_timer.run(); + new_tun->rx_timer.run(); } // Connect tunnels if forwarding is activated @@ -214,7 +356,7 @@ uint32_t gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t void gtpu::set_tunnel_status(uint32_t teidin, bool dl_active) { - tunnel* tun = get_tunnel(teidin); + gtpu_tunnel* tun = tunnels.find_tunnel(teidin); if (tun == nullptr) { logger.warning("Setting TEID=%d status", teidin); return; @@ -225,6 +367,11 @@ void gtpu::set_tunnel_status(uint32_t teidin, bool dl_active) if (dl_active and not old_state) { logger.info( "Activating GTPU tunnel rnti=0x%x,TEID=%d. %d SDUs currently buffered", tun->rnti, teidin, tun->buffer.size()); + std::stable_sort( + tun->buffer.begin(), + tun->buffer.end(), + [](const std::pair& lhs, + const std::pair& rhs) { return lhs.first < rhs.first; }); for (auto& sdu_it : tun->buffer) { pdcp->write_sdu( tun->rnti, tun->lcid, std::move(sdu_it.second), sdu_it.first == undefined_pdcp_sn ? -1 : sdu_it.first); @@ -235,84 +382,25 @@ void gtpu::set_tunnel_status(uint32_t teidin, bool dl_active) void gtpu::rem_bearer(uint16_t rnti, uint32_t lcid) { - auto ue_it = ue_teidin_db.find(rnti); - if (ue_it == ue_teidin_db.end()) { - logger.warning("Removing bearer rnti=0x%x, lcid=%d", rnti, lcid); - return; - } - std::vector& lcid_tuns = ue_it->second[lcid]; - - while (not lcid_tuns.empty()) { - rem_tunnel(lcid_tuns.back()); - } - logger.info("Removing bearer for rnti: 0x%x, lcid: %d", rnti, lcid); - - bool rem_ue = std::all_of( - ue_it->second.begin(), ue_it->second.end(), [](const std::vector& list) { return list.empty(); }); - if (rem_ue) { - ue_teidin_db.erase(ue_it); - } + tunnels.remove_bearer(rnti, lcid); } void gtpu::mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) { - logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti); - - if (ue_teidin_db.count(new_rnti) != 0) { - logger.error("New rnti already exists, aborting."); - return; - } - auto old_it = ue_teidin_db.find(old_rnti); - if (old_it == ue_teidin_db.end()) { - logger.error("Old rnti does not exist, aborting."); - return; - } - - // Change RNTI bearers map - ue_teidin_db.insert(std::make_pair(new_rnti, std::move(old_it->second))); - ue_teidin_db.erase(old_it); - - // Change TEID - auto new_it = ue_teidin_db.find(new_rnti); - for (auto& bearer : new_it->second) { - for (uint32_t teid : bearer) { - tunnels[teid].rnti = new_rnti; - } - } + tunnels.update_rnti(old_rnti, new_rnti); } void gtpu::rem_tunnel(uint32_t teidin) { - auto it = tunnels.find(teidin); - if (it == tunnels.end()) { - logger.warning("Removing GTPU tunnel TEID In=0x%x", teidin); - return; - } - tunnel& tun = it->second; - auto ue_it = ue_teidin_db.find(tun.rnti); - srsran_assert(ue_it != ue_teidin_db.end(), "ue_teidin_db must be consistent with tunnels data structure"); - std::vector& lcid_tunnels = ue_it->second[tun.lcid]; - lcid_tunnels.erase(std::remove(lcid_tunnels.begin(), lcid_tunnels.end(), teidin), lcid_tunnels.end()); - logger.info("TEID In=%d for rnti=0x%x erased", teidin, tun.rnti); - tunnels.erase(it); + tunnels.remove_tunnel(teidin); } void gtpu::rem_user(uint16_t rnti) { - logger.info("Removing rnti=0x%x", rnti); - auto ue_it = ue_teidin_db.find(rnti); - if (ue_it == ue_teidin_db.end()) { - logger.warning("Removing user: rnti=0x%x does not exist", rnti); - return; - } - for (auto& bearer : ue_it->second) { - while (not bearer.empty()) { - rem_tunnel(bearer.back()); - } - } + tunnels.remove_rnti(rnti); } -void gtpu::handle_end_marker(tunnel& rx_tunnel) +void gtpu::handle_end_marker(gtpu_tunnel& rx_tunnel) { uint16_t rnti = rx_tunnel.rnti; logger.info("Received GTPU End Marker for rnti=0x%x.", rnti); @@ -325,20 +413,15 @@ void gtpu::handle_end_marker(tunnel& rx_tunnel) rem_tunnel(rx_tunnel.teid_in); } else { - // TeNB switches paths, and flush PDUs that have been buffered - auto rnti_it = ue_teidin_db.find(rnti); - if (rnti_it == ue_teidin_db.end()) { - logger.error("No rnti=0x%x entry for associated TEID=%d", rnti, rx_tunnel.teid_in); - return; - } - std::vector& bearer_tunnels = rnti_it->second[rx_tunnel.lcid]; - for (uint32_t new_teidin : bearer_tunnels) { - tunnel& new_tun = tunnels.at(new_teidin); - if (new_teidin != rx_tunnel.teid_in and new_tun.prior_teid_in_present and - new_tun.prior_teid_in == rx_tunnel.teid_in) { - rem_tunnel(new_tun.prior_teid_in); - new_tun.prior_teid_in_present = false; - set_tunnel_status(new_tun.teid_in, true); + // TeNB switches paths, and flushes PDUs that have been buffered + srsran::span lcid_tunnels = tunnels.find_rnti_lcid_tunnels(rnti, rx_tunnel.lcid); + for (uint32_t new_teidin : lcid_tunnels) { + gtpu_tunnel* new_tun = tunnels.find_tunnel(new_teidin); + if (new_teidin != rx_tunnel.teid_in and new_tun->prior_teid_in_present and + new_tun->prior_teid_in == rx_tunnel.teid_in) { + rem_tunnel(new_tun->prior_teid_in); + new_tun->prior_teid_in_present = false; + set_tunnel_status(new_tun->teid_in, true); break; } } @@ -349,12 +432,6 @@ void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const soc { srsran_assert(pdu != nullptr, "Called with null PDU"); - struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; - if (ip_pkt->version != 4 && ip_pkt->version != 6) { - logger.error("Received Packet with invalid IP version=%d", (int)ip_pkt->version); - return; - } - logger.debug("Received %d bytes from S1-U interface", pdu->N_bytes); pdu->set_timestamp(); @@ -371,26 +448,20 @@ void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const soc } // Find TEID present in GTPU Header - auto tun_it = tunnels.find(header.teid); - if (tun_it == tunnels.end()) { + gtpu_tunnel* tun_ptr = tunnels.find_tunnel(header.teid); + if (tun_ptr == nullptr) { // Received G-PDU for non-existing and non-zero TEID. // Sending GTP-U error indication error_indication(addr.sin_addr.s_addr, addr.sin_port, header.teid); return; } - tunnel& rx_tunnel = tun_it->second; - - if (tun_it->second.rx_timer.is_valid()) { - // Restart Rx timer - tun_it->second.rx_timer.run(); - } switch (header.message_type) { case GTPU_MSG_DATA_PDU: { - handle_msg_data_pdu(header, rx_tunnel, std::move(pdu)); + handle_msg_data_pdu(header, *tun_ptr, std::move(pdu)); } break; case GTPU_MSG_END_MARKER: - handle_end_marker(rx_tunnel); + handle_end_marker(*tun_ptr); break; default: logger.warning("Unhandled GTPU message type=%d", header.message_type); @@ -398,8 +469,19 @@ void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const soc } } -void gtpu::handle_msg_data_pdu(const gtpu_header_t& header, tunnel& rx_tunnel, srsran::unique_byte_buffer_t pdu) +void gtpu::handle_msg_data_pdu(const gtpu_header_t& header, gtpu_tunnel& rx_tunnel, srsran::unique_byte_buffer_t pdu) { + struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; + if (ip_pkt->version != 4 && ip_pkt->version != 6) { + logger.error("Received SDU with invalid IP version=%d", (int)ip_pkt->version); + return; + } + + if (rx_tunnel.rx_timer.is_valid()) { + // Restart Rx timer + rx_tunnel.rx_timer.run(); + } + uint16_t rnti = rx_tunnel.rnti; uint16_t lcid = rx_tunnel.lcid; @@ -407,13 +489,12 @@ void gtpu::handle_msg_data_pdu(const gtpu_header_t& header, tunnel& rx_tunnel, s if (rx_tunnel.fwd_teid_in_present) { // Forward SDU to direct/indirect tunnel during Handover - auto tx_tun_it = tunnels.find(rx_tunnel.fwd_teid_in); - if (tx_tun_it == tunnels.end()) { + gtpu_tunnel* tx_tun_ptr = tunnels.find_tunnel(rx_tunnel.fwd_teid_in); + if (tx_tun_ptr == nullptr) { logger.error("Forwarding tunnel TEID=%d does not exist", rx_tunnel.fwd_teid_in); return; } - tunnel& tx_tun = tx_tun_it->second; - send_pdu_to_tunnel(tx_tun, std::move(pdu)); + send_pdu_to_tunnel(*tx_tun_ptr, std::move(pdu)); } else { // Forward SDU to PDCP or buffer it if tunnel is disabled @@ -422,7 +503,7 @@ void gtpu::handle_msg_data_pdu(const gtpu_header_t& header, tunnel& rx_tunnel, s pdcp_sn = (header.ext_buffer[1] << 8U) + header.ext_buffer[2]; } if (not rx_tunnel.dl_enabled) { - rx_tunnel.buffer.insert(std::make_pair(pdcp_sn, std::move(pdu))); + rx_tunnel.buffer.push_back(std::make_pair(pdcp_sn, std::move(pdu))); } else { pdcp->write_sdu(rnti, lcid, std::move(pdu), pdcp_sn == undefined_pdcp_sn ? -1 : (int)pdcp_sn); } @@ -437,29 +518,28 @@ void gtpu::handle_gtpu_m1u_rx_packet(srsran::unique_byte_buffer_t pdu, const soc /// Connect created tunnel with pre-existing tunnel for data forwarding int gtpu::create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in) { - auto rx_tun_pair = tunnels.find(rx_teid_in); - auto tx_tun_pair = tunnels.find(tx_teid_in); - if (rx_tun_pair == tunnels.end() or tx_tun_pair == tunnels.end()) { + gtpu_tunnel* rx_tun = tunnels.find_tunnel(rx_teid_in); + gtpu_tunnel* tx_tun = tunnels.find_tunnel(tx_teid_in); + if (rx_tun == nullptr or tx_tun == nullptr) { logger.error("Failed to create forwarding tunnel between teids 0x%x and 0x%x", rx_teid_in, tx_teid_in); return SRSRAN_ERROR; } - tunnel &rx_tun = rx_tun_pair->second, &tx_tun = tx_tun_pair->second; - rx_tun.fwd_teid_in_present = true; - rx_tun.fwd_teid_in = tx_teid_in; + rx_tun->fwd_teid_in_present = true; + rx_tun->fwd_teid_in = tx_teid_in; logger.info("Creating forwarding tunnel for rnti=0x%x, lcid=%d, in={0x%x, 0x%x}->out={0x%x, 0x%x}", - rx_tun.rnti, - rx_tun.lcid, - rx_tun.teid_out, - rx_tun.spgw_addr, - tx_tun.teid_out, - tx_tun.spgw_addr); + rx_tun->rnti, + rx_tun->lcid, + rx_tun->teid_out, + rx_tun->spgw_addr, + tx_tun->teid_out, + tx_tun->spgw_addr); // Get all buffered PDCP PDUs, and forward them through tx tunnel - std::map pdus = pdcp->get_buffered_pdus(rx_tun.rnti, rx_tun.lcid); + std::map pdus = pdcp->get_buffered_pdus(rx_tun->rnti, rx_tun->lcid); for (auto& pdu_pair : pdus) { - log_message(tx_tun, false, srsran::make_span(pdu_pair.second), pdu_pair.first); - send_pdu_to_tunnel(tx_tun, std::move(pdu_pair.second), pdu_pair.first); + log_message(*tx_tun, false, srsran::make_span(pdu_pair.second), pdu_pair.first); + send_pdu_to_tunnel(*tx_tun, std::move(pdu_pair.second), pdu_pair.first); } return SRSRAN_SUCCESS; @@ -538,12 +618,11 @@ void gtpu::echo_response(in_addr_t addr, in_port_t port, uint16_t seq) bool gtpu::end_marker(uint32_t teidin) { logger.info("TX GTPU End Marker."); - auto it = tunnels.find(teidin); - if (it == tunnels.end()) { + const gtpu_tunnel* tx_tun = tunnels.find_tunnel(teidin); + if (tx_tun == nullptr) { logger.error("TEID=%d not found to send the end marker to", teidin); return false; } - tunnel& tunnel = it->second; gtpu_header_t header = {}; unique_byte_buffer_t pdu = make_byte_buffer(); @@ -555,14 +634,14 @@ bool gtpu::end_marker(uint32_t teidin) // header header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL; header.message_type = GTPU_MSG_END_MARKER; - header.teid = tunnel.teid_out; + header.teid = tx_tun->teid_out; header.length = 0; gtpu_write_header(&header, pdu.get(), logger); struct sockaddr_in servaddr = {}; servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = htonl(tunnel.spgw_addr); + servaddr.sin_addr.s_addr = htonl(tx_tun->spgw_addr); servaddr.sin_port = htons(GTPU_PORT); sendto(fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in)); @@ -573,24 +652,7 @@ bool gtpu::end_marker(uint32_t teidin) * TEID to RNTI/LCID helper functions ***************************************************************************/ -gtpu::tunnel* gtpu::get_tunnel(uint32_t teidin) -{ - auto it = tunnels.find(teidin); - return it != tunnels.end() ? &it->second : nullptr; -} - -srsran::span gtpu::get_lcid_teids(uint16_t rnti, uint32_t lcid) -{ - auto ue_it = ue_teidin_db.find(rnti); - if (ue_it == ue_teidin_db.end() or lcid < SRSENB_N_SRB or lcid >= SRSENB_N_RADIO_BEARERS or - ue_it->second[lcid].empty()) { - logger.error("Could not find bearer rnti=0x%x, lcid=%d", rnti, lcid); - return {}; - } - return ue_it->second[lcid]; -} - -void gtpu::log_message(tunnel& tun, bool is_rx, srsran::span pdu, int pdcp_sn) +void gtpu::log_message(const gtpu_tunnel& tun, bool is_rx, srsran::span pdu, int pdcp_sn) { struct iphdr* ip_pkt = (struct iphdr*)pdu.data(); if (ip_pkt->version != 4 && ip_pkt->version != 6) { @@ -611,10 +673,11 @@ void gtpu::log_message(tunnel& tun, bool is_rx, srsran::span pdu, int p if (not tun.dl_enabled) { fmt::format_to(strbuf2, "DL (buffered), "); } else if (tun.fwd_teid_in_present) { - tunnel& tx_tun = tunnels.at(tun.fwd_teid_in); + const gtpu_tunnel* tx_tun = tunnels.find_tunnel(tun.fwd_teid_in); + srsran_assert(tx_tun != nullptr, "Invalid teid=%d", tun.fwd_teid_in); addrbuf.clear(); - srsran::gtpu_ntoa(addrbuf, htonl(tx_tun.spgw_addr)); - fmt::format_to(strbuf2, "{}:0x{:0x} (forwarded), ", srsran::to_c_str(addrbuf), tx_tun.teid_in); + srsran::gtpu_ntoa(addrbuf, htonl(tx_tun->spgw_addr)); + fmt::format_to(strbuf2, "{}:0x{:0x} (forwarded), ", srsran::to_c_str(addrbuf), tx_tun->teid_in); } else { fmt::format_to(strbuf2, "DL, "); } diff --git a/srsenb/test/upper/gtpu_test.cc b/srsenb/test/upper/gtpu_test.cc index 5c2e08e4e..74d4ccc7f 100644 --- a/srsenb/test/upper/gtpu_test.cc +++ b/srsenb/test/upper/gtpu_test.cc @@ -28,7 +28,7 @@ static const size_t PDU_HEADER_SIZE = 20; class stack_tester : public stack_interface_gtpu_lte { public: - int s1u_fd; + int s1u_fd = -1; void add_gtpu_s1u_socket_handler(int fd) { s1u_fd = fd; } void add_gtpu_m1u_socket_handler(int fd) {} }; @@ -127,7 +127,9 @@ srsran::unique_byte_buffer_t read_socket(int fd) return pdu; } -int test_gtpu_direct_tunneling() +enum class tunnel_test_event { success, wait_end_marker_timeout }; + +int test_gtpu_direct_tunneling(tunnel_test_event event) { uint16_t rnti = 0x46, rnti2 = 0x50; uint32_t drb1 = 3; @@ -236,10 +238,19 @@ int test_gtpu_direct_tunneling() TESTASSERT(tenb_pdcp.last_sdu->N_bytes == encoded_data.size() and memcmp(tenb_pdcp.last_sdu->msg, encoded_data.data(), encoded_data.size()) == 0); tenb_pdcp.clear(); - // EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed - pdu = encode_end_marker(senb_teid_in); - senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr); - tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); + + TESTASSERT(tenb_pdcp.last_sdu == nullptr); + if (event == tunnel_test_event::wait_end_marker_timeout) { + // TEST: EndMarker does not reach TeNB, but there is a timeout that will resume the new GTPU tunnel + for (size_t i = 0; i < 1000; ++i) { + task_sched.tic(); + } + } else { + // TEST: EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed + pdu = encode_end_marker(senb_teid_in); + senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr); + tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); + } srsran::span encoded_data2{tenb_pdcp.last_sdu->msg + 20u, tenb_pdcp.last_sdu->msg + 30u}; TESTASSERT(std::all_of(encoded_data2.begin(), encoded_data2.end(), [N_pdus](uint8_t b) { return b == N_pdus - 1; })); @@ -248,7 +259,7 @@ int test_gtpu_direct_tunneling() } // namespace srsenb -int main() +int main(int argc, char** argv) { // Setup logging. auto& logger = srslog::fetch_basic_logger("GTPU", false); @@ -256,9 +267,10 @@ int main() logger.set_hex_dump_max_size(-1); // Start the log backend. - srslog::init(); + srsran::test_init(argc, argv); - TESTASSERT(srsenb::test_gtpu_direct_tunneling() == SRSRAN_SUCCESS); + 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); srslog::flush();