gtpu refactor - use of raii to automatic path switch and teid removal in forwarding. Clean up of gtpu_tunnel_manager methods.

master
Francisco 4 years ago committed by Francisco Paisana
parent ab2bd9a2d2
commit 44440cf562

@ -37,23 +37,39 @@ namespace srsenb {
class pdcp_interface_gtpu; class pdcp_interface_gtpu;
class stack_interface_gtpu_lte; class stack_interface_gtpu_lte;
struct gtpu_tunnel {
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;
};
class gtpu_tunnel_manager class gtpu_tunnel_manager
{ {
using buffered_sdu_list = srsran::bounded_vector<std::pair<uint32_t, srsran::unique_byte_buffer_t>, 512>;
public: public:
const static size_t MAX_TUNNELS_PER_UE = 4; const static size_t MAX_TUNNELS_PER_UE = 4;
enum class tunnel_state { pdcp_active, buffering, forward_to, forwarded_from };
struct tunnel {
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;
tunnel_state state = tunnel_state::pdcp_active;
srsran::unique_timer rx_timer;
srsran::byte_buffer_pool_ptr<buffered_sdu_list> buffer;
tunnel* fwd_tunnel = nullptr; ///< forward Rx SDUs to this TEID
srsran::move_callback<void()> on_removal;
tunnel() = default;
tunnel(tunnel&&) noexcept = default;
tunnel& operator=(tunnel&&) noexcept = default;
~tunnel()
{
if (not on_removal.is_empty()) {
on_removal();
}
}
};
struct lcid_tunnel { struct lcid_tunnel {
uint32_t lcid; uint32_t lcid;
uint32_t teid; uint32_t teid;
@ -66,18 +82,23 @@ public:
}; };
using ue_lcid_tunnel_list = srsran::bounded_vector<lcid_tunnel, MAX_TUNNELS_PER_UE>; using ue_lcid_tunnel_list = srsran::bounded_vector<lcid_tunnel, MAX_TUNNELS_PER_UE>;
gtpu_tunnel_manager(); explicit gtpu_tunnel_manager(srsran::task_sched_handle task_sched_);
void init(pdcp_interface_gtpu* pdcp_);
gtpu_tunnel* find_tunnel(uint32_t teid); bool has_teid(uint32_t teid) const { return tunnels.contains(teid); }
const tunnel* find_tunnel(uint32_t teid);
ue_lcid_tunnel_list* find_rnti_tunnels(uint16_t rnti); ue_lcid_tunnel_list* find_rnti_tunnels(uint16_t rnti);
srsran::span<lcid_tunnel> find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid); srsran::span<lcid_tunnel> find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid);
gtpu_tunnel* add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr); const tunnel* add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr);
bool update_rnti(uint16_t old_rnti, uint16_t new_rnti); bool update_rnti(uint16_t old_rnti, uint16_t new_rnti);
int set_tunnel_dl_state(uint32_t teid, bool state, pdcp_interface_gtpu* pdcp); void activate_tunnel(uint32_t teid);
srsran::expected<bool> get_tunnel_dl_state(uint32_t teid) const; void suspend_tunnel(uint32_t teid);
void buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsran::unique_byte_buffer_t sdu); void set_tunnel_priority(uint32_t first_teid, uint32_t second_teid);
tunnel_state handle_rx_pdcp_sdu(uint32_t teid);
void buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsran::unique_byte_buffer_t sdu);
void setup_forwarding(uint32_t rx_teid, uint32_t tx_teid);
bool remove_tunnel(uint32_t teid); bool remove_tunnel(uint32_t teid);
bool remove_bearer(uint16_t rnti, uint32_t lcid); bool remove_bearer(uint16_t rnti, uint32_t lcid);
@ -85,22 +106,20 @@ public:
private: private:
const uint32_t undefined_pdcp_sn = std::numeric_limits<uint32_t>::max(); const uint32_t undefined_pdcp_sn = std::numeric_limits<uint32_t>::max();
using tunnel_list_t = srsran::static_id_obj_pool<uint32_t, tunnel, SRSENB_MAX_UES * MAX_TUNNELS_PER_UE>;
using tunnel_ctxt_it = typename tunnel_list_t::iterator;
struct tunnel_ctxt { srsran::task_sched_handle task_sched;
gtpu_tunnel params; pdcp_interface_gtpu* pdcp = nullptr;
bool dl_enabled = true; srslog::basic_logger& logger;
srsran::byte_buffer_pool_ptr<srsran::bounded_vector<std::pair<uint32_t, srsran::unique_byte_buffer_t>, 512> >
buffer;
};
using tunnel_list_t = srsran::static_id_obj_pool<uint32_t, tunnel_ctxt, SRSENB_MAX_UES * MAX_TUNNELS_PER_UE>;
using tunnel_ctxt_it = typename tunnel_list_t::iterator;
srslog::basic_logger& logger;
tunnel_list_t tunnels; tunnel_list_t tunnels;
srsran::static_circular_map<uint16_t, ue_lcid_tunnel_list, SRSENB_MAX_UES> ue_teidin_db; srsran::static_circular_map<uint16_t, ue_lcid_tunnel_list, SRSENB_MAX_UES> ue_teidin_db;
}; };
using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state;
using gtpu_tunnel = gtpu_tunnel_manager::tunnel;
class gtpu final : public gtpu_interface_rrc, public gtpu_interface_pdcp class gtpu final : public gtpu_interface_rrc, public gtpu_interface_pdcp
{ {
public: public:
@ -190,8 +209,9 @@ private:
bool send_end_marker(uint32_t teidin); bool send_end_marker(uint32_t teidin);
void handle_end_marker(const gtpu_tunnel& rx_tunnel); void handle_end_marker(const gtpu_tunnel& rx_tunnel);
void void handle_msg_data_pdu(const srsran::gtpu_header_t& header,
handle_msg_data_pdu(const srsran::gtpu_header_t& header, gtpu_tunnel& rx_tunnel, srsran::unique_byte_buffer_t pdu); const 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); int create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in);

@ -21,7 +21,6 @@
#include <errno.h> #include <errno.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <stdio.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
@ -29,12 +28,19 @@ using namespace srsran;
namespace srsenb { namespace srsenb {
gtpu_tunnel_manager::gtpu_tunnel_manager() : logger(srslog::fetch_basic_logger("GTPU")) {} gtpu_tunnel_manager::gtpu_tunnel_manager(srsran::task_sched_handle task_sched_) :
logger(srslog::fetch_basic_logger("GTPU")), task_sched(task_sched_)
{}
void gtpu_tunnel_manager::init(pdcp_interface_gtpu* pdcp_)
{
pdcp = pdcp_;
}
gtpu_tunnel* gtpu_tunnel_manager::find_tunnel(uint32_t teid) const gtpu_tunnel_manager::tunnel* gtpu_tunnel_manager::find_tunnel(uint32_t teid)
{ {
auto it = tunnels.find(teid); auto it = tunnels.find(teid);
return it != tunnels.end() ? &it->second.params : nullptr; return it != tunnels.end() ? &it->second : nullptr;
} }
gtpu_tunnel_manager::ue_lcid_tunnel_list* gtpu_tunnel_manager::find_rnti_tunnels(uint16_t rnti) gtpu_tunnel_manager::ue_lcid_tunnel_list* gtpu_tunnel_manager::find_rnti_tunnels(uint16_t rnti)
@ -61,23 +67,23 @@ srsran::span<gtpu_tunnel_manager::lcid_tunnel> gtpu_tunnel_manager::find_rnti_lc
return srsran::span<lcid_tunnel>(&(*lcid_it_begin), &(*lcid_it_end)); return srsran::span<lcid_tunnel>(&(*lcid_it_begin), &(*lcid_it_end));
} }
gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr) const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr)
{ {
if (lcid < SRSENB_N_SRB or lcid >= SRSENB_N_RADIO_BEARERS) { if (lcid < SRSENB_N_SRB or lcid >= SRSENB_N_RADIO_BEARERS) {
logger.warning("Adding TEID with invalid parmaters"); logger.warning("Adding TEID with invalid lcid=%d", lcid);
return nullptr; return nullptr;
} }
auto ret_pair = tunnels.insert(tunnel_ctxt()); auto ret_pair = tunnels.insert(tunnel());
if (not ret_pair) { if (not ret_pair) {
logger.warning("Adding new GTPU TEID In"); logger.warning("Unable to create new GTPU TEID In");
return nullptr; return nullptr;
} }
tunnel_ctxt* tun = &tunnels[ret_pair.value()]; tunnel* tun = &tunnels[ret_pair.value()];
tun->params.teid_in = ret_pair.value(); tun->teid_in = ret_pair.value();
tun->params.rnti = rnti; tun->rnti = rnti;
tun->params.lcid = lcid; tun->lcid = lcid;
tun->params.teid_out = teidout; tun->teid_out = teidout;
tun->params.spgw_addr = spgw_addr; tun->spgw_addr = spgw_addr;
if (not ue_teidin_db.contains(rnti)) { if (not ue_teidin_db.contains(rnti)) {
ue_teidin_db.insert(rnti, ue_lcid_tunnel_list()); ue_teidin_db.insert(rnti, ue_lcid_tunnel_list());
@ -86,31 +92,29 @@ gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid, uint3
if (ue_tunnels.full()) { if (ue_tunnels.full()) {
logger.error("The number of TEIDs per UE exceeded for rnti=0x%x", rnti); logger.error("The number of TEIDs per UE exceeded for rnti=0x%x", rnti);
tunnels.erase(tun->params.teid_in); tunnels.erase(tun->teid_in);
return nullptr; return nullptr;
} }
ue_tunnels.push_back(lcid_tunnel{lcid, tun->params.teid_in}); ue_tunnels.push_back(lcid_tunnel{lcid, tun->teid_in});
std::sort(ue_tunnels.begin(), ue_tunnels.end()); std::sort(ue_tunnels.begin(), ue_tunnels.end());
fmt::memory_buffer str_buffer; fmt::memory_buffer str_buffer;
srsran::gtpu_ntoa(str_buffer, htonl(spgw_addr)); srsran::gtpu_ntoa(str_buffer, htonl(spgw_addr));
logger.info("New tunnel teid_in=0x%x, teid_out=0x%x, rnti=0x%x, lcid=%d, addr=%s", logger.info("New tunnel teid_in=0x%x, teid_out=0x%x, rnti=0x%x, lcid=%d, addr=%s",
tun->params.teid_in, tun->teid_in,
teidout, teidout,
rnti, rnti,
lcid, lcid,
srsran::to_c_str(str_buffer)); srsran::to_c_str(str_buffer));
return &tun->params; return tun;
} }
bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti) bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti)
{ {
srsran_assert(find_rnti_tunnels(new_rnti) == nullptr, "New rnti=0x%x already exists", new_rnti);
auto* old_rnti_ptr = find_rnti_tunnels(old_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); logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti);
// Change RNTI bearers map // Change RNTI bearers map
@ -120,7 +124,7 @@ bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti)
// Change TEID in existing tunnels // Change TEID in existing tunnels
auto* new_rnti_ptr = find_rnti_tunnels(new_rnti); auto* new_rnti_ptr = find_rnti_tunnels(new_rnti);
for (lcid_tunnel& bearer : *new_rnti_ptr) { for (lcid_tunnel& bearer : *new_rnti_ptr) {
tunnels[bearer.teid].params.rnti = new_rnti; tunnels[bearer.teid].rnti = new_rnti;
} }
return true; return true;
@ -128,25 +132,16 @@ bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti)
bool gtpu_tunnel_manager::remove_tunnel(uint32_t teidin) bool gtpu_tunnel_manager::remove_tunnel(uint32_t teidin)
{ {
auto it = tunnels.find(teidin); tunnel& tun = tunnels[teidin];
if (it == tunnels.end()) {
logger.warning("Removing GTPU tunnel TEID In=0x%x", teidin);
return false;
}
tunnel_ctxt& tun = it->second;
// Erase any present forwarding tunnel
if (tun.params.fwd_teid_in_present) {
remove_tunnel(tun.params.fwd_teid_in);
}
// erase keeping the relative order // erase keeping the relative order
auto& ue = ue_teidin_db[tun.params.rnti]; auto& ue = ue_teidin_db[tun.rnti];
auto lcid_it = std::find(ue.begin(), ue.end(), lcid_tunnel{tun.params.lcid, tun.params.teid_in}); auto lcid_it = std::lower_bound(ue.begin(), ue.end(), lcid_tunnel{tun.lcid, tun.teid_in});
srsran_assert(lcid_it->teid == tun.teid_in and lcid_it->lcid == tun.lcid, "TEID in undefined state");
ue.erase(lcid_it); ue.erase(lcid_it);
logger.info("TEID In=%d for rnti=0x%x erased", teidin, tun.params.rnti); logger.info("TEID In=%d for rnti=0x%x erased", teidin, tun.rnti);
tunnels.erase(it); tunnels.erase(teidin);
return true; return true;
} }
@ -168,7 +163,7 @@ bool gtpu_tunnel_manager::remove_bearer(uint16_t rnti, uint32_t lcid)
bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti) bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti)
{ {
if (not ue_teidin_db.contains(rnti)) { if (not ue_teidin_db.contains(rnti)) {
logger.warning("Removing rnti. rnti=0x%x not found.", rnti); logger.warning("removing rnti. rnti=0x%x not found.", rnti);
return false; return false;
} }
logger.info("Removing rnti=0x%x", rnti); logger.info("Removing rnti=0x%x", rnti);
@ -180,63 +175,110 @@ bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti)
return true; return true;
} }
srsran::expected<bool> gtpu_tunnel_manager::get_tunnel_dl_state(uint32_t teid) const void gtpu_tunnel_manager::activate_tunnel(uint32_t teid)
{ {
auto it = tunnels.find(teid); tunnel& tun = tunnels[teid];
if (it == tunnels.end()) { if (tun.state == tunnel_state::pdcp_active) {
return srsran::default_error_t(); // nothing happens
return;
}
logger.info("Activating GTPU tunnel rnti=0x%x,TEID=%d. %d SDUs currently buffered",
tun.rnti,
tun.teid_in,
tun.buffer->size());
// Forward buffered SDUs to lower layers and delete buffer
auto lower_sn = [](const std::pair<uint32_t, srsran::unique_byte_buffer_t>& lhs,
const std::pair<uint32_t, srsran::unique_byte_buffer_t>& rhs) { return lhs.first < rhs.first; };
std::stable_sort(tun.buffer->begin(), tun.buffer->end(), lower_sn);
for (auto& sdu_pair : *tun.buffer) {
uint32_t pdcp_sn = sdu_pair.first;
pdcp->write_sdu(tun.rnti, tun.lcid, std::move(sdu_pair.second), pdcp_sn == undefined_pdcp_sn ? -1 : pdcp_sn);
} }
bool is_enabled = it->second.dl_enabled; tun.buffer.reset();
return is_enabled; tun.state = tunnel_state::pdcp_active;
} }
int gtpu_tunnel_manager::set_tunnel_dl_state(uint32_t teid, bool state, pdcp_interface_gtpu* pdcp) void gtpu_tunnel_manager::suspend_tunnel(uint32_t teid)
{ {
auto it = tunnels.find(teid); tunnel& tun = tunnels[teid];
if (it == tunnels.end()) { if (tun.state != tunnel_state::pdcp_active) {
logger.warning("Setting tunnel DL state. TEID=%d not found", teid); logger.error("Invalid TEID transition detected");
return SRSRAN_ERROR; return;
} }
tunnel_ctxt& tun = it->second; // Create a container for buffering SDUs
tun.buffer.emplace();
if (tun.dl_enabled == state) { tun.state = tunnel_state::buffering;
// state didn't change }
return SRSRAN_SUCCESS;
} void gtpu_tunnel_manager::set_tunnel_priority(uint32_t before_teid, uint32_t after_teid)
{
tun.dl_enabled = state; tunnel& before_tun = tunnels[before_teid];
if (state) { tunnel& after_tun = tunnels[after_teid];
logger.info("Activating GTPU tunnel rnti=0x%x,TEID=%d. %d SDUs currently buffered",
tun.params.rnti, // GTPU should not forward SDUs from main tunnel until the SeNB-TeNB tunnel has been flushed
tun.params.teid_in, suspend_tunnel(after_teid);
tun.buffer->size());
// Forward buffered SDUs to lower layers and delete buffer before_tun.on_removal = [this, after_teid]() {
if (pdcp != nullptr) { if (tunnels.contains(after_teid)) {
auto lower_sn = [](const std::pair<uint32_t, srsran::unique_byte_buffer_t>& lhs, // In Handover, TeNB switches paths, and flushes PDUs that have been buffered
const std::pair<uint32_t, srsran::unique_byte_buffer_t>& rhs) { activate_tunnel(after_teid);
return lhs.first < rhs.first;
};
std::stable_sort(tun.buffer->begin(), tun.buffer->end(), lower_sn);
for (auto& sdu_pair : *tun.buffer) {
uint32_t pdcp_sn = sdu_pair.first;
pdcp->write_sdu(
tun.params.rnti, tun.params.lcid, std::move(sdu_pair.second), pdcp_sn == undefined_pdcp_sn ? -1 : pdcp_sn);
}
} }
tun.buffer.reset(); };
} else {
// Create a container for buffering SDUs // Schedule auto-removal of this indirect tunnel
tun.buffer.emplace(); before_tun.rx_timer = task_sched.get_unique_timer();
before_tun.rx_timer.set(500, [this, before_teid](uint32_t tid) {
// This will self-destruct the callback object
remove_tunnel(before_teid);
});
before_tun.rx_timer.run();
}
gtpu_tunnel_manager::tunnel_state gtpu_tunnel_manager::handle_rx_pdcp_sdu(uint32_t teid)
{
tunnel& rx_tun = tunnels[teid];
// Reset Rx timer when a PDCP SDU is received
if (rx_tun.rx_timer.is_valid() and rx_tun.rx_timer.is_running()) {
rx_tun.rx_timer.run();
} }
return SRSRAN_SUCCESS; return rx_tun.state;
} }
void gtpu_tunnel_manager::buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsran::unique_byte_buffer_t sdu) void gtpu_tunnel_manager::buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsran::unique_byte_buffer_t sdu)
{ {
srsran_assert(not tunnels[teid].dl_enabled, "Method cannot be called for inexistent TEID=%d", teid); tunnel& rx_tun = tunnels[teid];
tunnels[teid].buffer->push_back(std::make_pair(pdcp_sn, std::move(sdu)));
srsran_assert(rx_tun.state == tunnel_state::buffering, "Buffering of PDCP SDUs only enabled when PDCP is not active");
rx_tun.buffer->push_back(std::make_pair(pdcp_sn, std::move(sdu)));
}
void gtpu_tunnel_manager::setup_forwarding(uint32_t rx_teid, uint32_t tx_teid)
{
tunnel& rx_tun = tunnels[rx_teid];
tunnel& tx_tun = tunnels[tx_teid];
rx_tun.state = tunnel_state::forward_to;
rx_tun.fwd_tunnel = &tx_tun;
tx_tun.state = tunnel_state::forwarded_from;
// Auto-removes indirect tunnel when the main tunnel is removed
rx_tun.on_removal = [this, tx_teid]() {
if (tunnels.contains(tx_teid)) {
remove_tunnel(tx_teid);
}
};
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);
} }
/******************** /********************
@ -244,7 +286,7 @@ void gtpu_tunnel_manager::buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsra
*******************/ *******************/
gtpu::gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) : gtpu::gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) :
m1u(this), task_sched(task_sched_), logger(logger) m1u(this), task_sched(task_sched_), logger(logger), tunnels(task_sched_)
{} {}
gtpu::~gtpu() gtpu::~gtpu()
@ -265,6 +307,8 @@ int gtpu::init(std::string gtp_bind_addr_,
mme_addr = mme_addr_; mme_addr = mme_addr_;
stack = stack_; stack = stack_;
tunnels.init(pdcp);
char errbuf[128] = {}; char errbuf[128] = {};
// Set up socket // Set up socket
@ -321,10 +365,10 @@ void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t
{ {
srsran::span<gtpu_tunnel_manager::lcid_tunnel> teids = tunnels.find_rnti_lcid_tunnels(rnti, lcid); srsran::span<gtpu_tunnel_manager::lcid_tunnel> teids = tunnels.find_rnti_lcid_tunnels(rnti, lcid);
if (teids.empty()) { if (teids.empty()) {
logger.warning("The rnti=0x%x,lcid=%d does not have any active tunnel", rnti, lcid); logger.warning("The rnti=0x%x,lcid=%d does not have any pdcp_active tunnel", rnti, lcid);
return; return;
} }
gtpu_tunnel& tx_tun = *tunnels.find_tunnel(teids[0].teid); const gtpu_tunnel& tx_tun = *tunnels.find_tunnel(teids[0].teid);
log_message(tx_tun, false, srsran::make_span(pdu)); log_message(tx_tun, false, srsran::make_span(pdu));
send_pdu_to_tunnel(tx_tun, std::move(pdu)); send_pdu_to_tunnel(tx_tun, std::move(pdu));
} }
@ -371,7 +415,7 @@ void gtpu::send_pdu_to_tunnel(const gtpu_tunnel& tx_tun, srsran::unique_byte_buf
uint32_t gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props) 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 // Allocate a TEID for the incoming tunnel
gtpu_tunnel* new_tun = tunnels.add_tunnel(rnti, lcid, teid_out, addr); const gtpu_tunnel* new_tun = tunnels.add_tunnel(rnti, lcid, teid_out, addr);
if (new_tun == nullptr) { if (new_tun == nullptr) {
return -1; return -1;
} }
@ -380,44 +424,17 @@ uint32_t gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t
if (props != nullptr) { if (props != nullptr) {
if (props->flush_before_teidin_present) { if (props->flush_before_teidin_present) {
// GTPU should wait for the bearer ctxt to arrive before sending SDUs from DL tunnel to PDCP // GTPU should wait for the bearer ctxt to arrive before sending SDUs from DL tunnel to PDCP
tunnels.set_tunnel_dl_state(teid_in, false, nullptr); tunnels.suspend_tunnel(teid_in);
// GTPU should not forward SDUs from main tunnel until the SeNB-TeNB tunnel has been flushed // GTPU should not forward SDUs from main tunnel until the SeNB-TeNB tunnel has been flushed
gtpu_tunnel* after_tun = tunnels.find_tunnel(props->flush_before_teidin); tunnels.set_tunnel_priority(teid_in, 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;
}
tunnels.set_tunnel_dl_state(after_tun->teid_in, false, nullptr);
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) {
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 {
logger.info("Callback to automatic indirect tunnel deletion called for non-existent TEID=%d", after_teidin);
}
// This will self-destruct the callback object
rem_tunnel(before_teidin);
});
new_tun->rx_timer.run();
} }
// Connect tunnels if forwarding is activated // Connect tunnels if forwarding is activated
if (props->forward_from_teidin_present) { if (props->forward_from_teidin_present) {
if (create_dl_fwd_tunnel(props->forward_from_teidin, teid_in) != SRSRAN_SUCCESS) { if (create_dl_fwd_tunnel(props->forward_from_teidin, teid_in) != SRSRAN_SUCCESS) {
rem_tunnel(teid_in); rem_tunnel(teid_in);
return 0; return -1;
} }
} }
} }
@ -427,53 +444,68 @@ 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) void gtpu::set_tunnel_status(uint32_t teidin, bool dl_active)
{ {
tunnels.set_tunnel_dl_state(teidin, dl_active, pdcp); if (not tunnels.has_teid(teidin)) {
logger.error("Setting status for non-existent TEID In=0x%x", teidin);
return;
}
if (dl_active) {
tunnels.activate_tunnel(teidin);
} else {
tunnels.suspend_tunnel(teidin);
}
} }
void gtpu::rem_bearer(uint16_t rnti, uint32_t lcid) void gtpu::rem_bearer(uint16_t rnti, uint32_t lcid)
{ {
if (tunnels.find_rnti_lcid_tunnels(rnti, lcid).empty()) {
logger.error("Removing non-existent bearer rnti=0x%x,lcid=%d", rnti, lcid);
return;
}
tunnels.remove_bearer(rnti, lcid); tunnels.remove_bearer(rnti, lcid);
} }
void gtpu::mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) void gtpu::mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti)
{ {
auto* old_rnti_ptr = tunnels.find_rnti_tunnels(old_rnti);
if (old_rnti_ptr == nullptr or tunnels.find_rnti_tunnels(new_rnti) != nullptr) {
logger.error("Modifying bearer rnti. Old rnti=0x%x, new rnti=0x%x", old_rnti, new_rnti);
return;
}
tunnels.update_rnti(old_rnti, new_rnti); tunnels.update_rnti(old_rnti, new_rnti);
} }
void gtpu::rem_tunnel(uint32_t teidin) void gtpu::rem_tunnel(uint32_t teidin)
{ {
if (not tunnels.has_teid(teidin)) {
logger.warning("TEID In=0x%x to be removed does not exist", teidin);
return;
}
tunnels.remove_tunnel(teidin); tunnels.remove_tunnel(teidin);
} }
void gtpu::rem_user(uint16_t rnti) void gtpu::rem_user(uint16_t rnti)
{ {
if (tunnels.find_rnti_tunnels(rnti) == nullptr) {
logger.warning("removing rnti. rnti=0x%x not found.", rnti);
return;
}
tunnels.remove_rnti(rnti); tunnels.remove_rnti(rnti);
} }
void gtpu::handle_end_marker(const gtpu_tunnel& rx_tunnel) void gtpu::handle_end_marker(const gtpu_tunnel& rx_tunnel)
{ {
uint16_t rnti = rx_tunnel.rnti; uint16_t rnti = rx_tunnel.rnti;
logger.info("Received GTPU End Marker for rnti=0x%x.", rnti); logger.info("Received GTPU End Marker for TEID In=0x%x, rnti=0x%x.", rx_tunnel.teid_in, rnti);
// TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover if (rx_tunnel.state == gtpu_tunnel_state::forward_to) {
if (rx_tunnel.fwd_teid_in_present) { // TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover
// END MARKER should be forwarded to TeNB if forwarding is activated // END MARKER should be forwarded to TeNB if forwarding is activated
send_end_marker(rx_tunnel.fwd_teid_in); send_end_marker(rx_tunnel.fwd_tunnel->teid_in);
rem_tunnel(rx_tunnel.teid_in);
} else {
// TeNB switches paths, and flushes PDUs that have been buffered
srsran::span<gtpu_tunnel_manager::lcid_tunnel> lcid_tunnels = tunnels.find_rnti_lcid_tunnels(rnti, rx_tunnel.lcid);
for (auto& lcid_tun : lcid_tunnels) {
gtpu_tunnel* new_tun = tunnels.find_tunnel(lcid_tun.teid);
if (new_tun->teid_in != 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;
}
}
} }
// Remove tunnel that received End Marker
rem_tunnel(rx_tunnel.teid_in);
} }
void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr) void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr)
@ -496,7 +528,7 @@ void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const soc
} }
// Find TEID present in GTPU Header // Find TEID present in GTPU Header
gtpu_tunnel* tun_ptr = tunnels.find_tunnel(header.teid); const gtpu_tunnel* tun_ptr = tunnels.find_tunnel(header.teid);
if (tun_ptr == nullptr) { if (tun_ptr == nullptr) {
// Received G-PDU for non-existing and non-zero TEID. // Received G-PDU for non-existing and non-zero TEID.
// Sending GTP-U error indication // Sending GTP-U error indication
@ -517,7 +549,9 @@ 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, gtpu_tunnel& rx_tunnel, srsran::unique_byte_buffer_t pdu) void gtpu::handle_msg_data_pdu(const gtpu_header_t& header,
const gtpu_tunnel& rx_tunnel,
srsran::unique_byte_buffer_t pdu)
{ {
struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; struct iphdr* ip_pkt = (struct iphdr*)pdu->msg;
if (ip_pkt->version != 4 && ip_pkt->version != 6) { if (ip_pkt->version != 4 && ip_pkt->version != 6) {
@ -525,9 +559,10 @@ void gtpu::handle_msg_data_pdu(const gtpu_header_t& header, gtpu_tunnel& rx_tunn
return; return;
} }
if (rx_tunnel.rx_timer.is_valid()) { // Forward SDU to PDCP or buffer it if tunnel is disabled
// Restart Rx timer uint32_t pdcp_sn = undefined_pdcp_sn;
rx_tunnel.rx_timer.run(); if ((header.flags & GTPU_FLAGS_EXTENDED_HDR) != 0 and header.next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER) {
pdcp_sn = (header.ext_buffer[1] << 8U) + header.ext_buffer[2];
} }
uint16_t rnti = rx_tunnel.rnti; uint16_t rnti = rx_tunnel.rnti;
@ -535,26 +570,26 @@ void gtpu::handle_msg_data_pdu(const gtpu_header_t& header, gtpu_tunnel& rx_tunn
log_message(rx_tunnel, true, srsran::make_span(pdu)); log_message(rx_tunnel, true, srsran::make_span(pdu));
if (rx_tunnel.fwd_teid_in_present) { gtpu_tunnel_manager::tunnel_state tun_state = tunnels.handle_rx_pdcp_sdu(rx_tunnel.teid_in);
// Forward SDU to direct/indirect tunnel during Handover
const 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;
}
send_pdu_to_tunnel(*tx_tun_ptr, std::move(pdu));
return;
}
// Forward SDU to PDCP or buffer it if tunnel is disabled switch (tun_state) {
uint32_t pdcp_sn = undefined_pdcp_sn; case gtpu_tunnel_manager::tunnel_state::forward_to: {
if ((header.flags & GTPU_FLAGS_EXTENDED_HDR) != 0 and header.next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER) { // Forward SDU to direct/indirect tunnel during Handover
pdcp_sn = (header.ext_buffer[1] << 8U) + header.ext_buffer[2]; send_pdu_to_tunnel(*rx_tunnel.fwd_tunnel, std::move(pdu));
} break;
if (not tunnels.get_tunnel_dl_state(rx_tunnel.teid_in).value()) { }
tunnels.buffer_pdcp_sdu(rx_tunnel.teid_in, pdcp_sn, std::move(pdu)); case gtpu_tunnel_manager::tunnel_state::buffering: {
} else { tunnels.buffer_pdcp_sdu(rx_tunnel.teid_in, pdcp_sn, std::move(pdu));
pdcp->write_sdu(rnti, lcid, std::move(pdu), pdcp_sn == undefined_pdcp_sn ? -1 : (int)pdcp_sn); break;
}
case gtpu_tunnel_manager::tunnel_state::pdcp_active: {
pdcp->write_sdu(rnti, lcid, std::move(pdu), pdcp_sn == undefined_pdcp_sn ? -1 : (int)pdcp_sn);
break;
}
case gtpu_tunnel_manager::tunnel_state::forwarded_from:
default:
logger.error("TEID=0x%x in invalid state");
break;
} }
} }
@ -566,28 +601,21 @@ 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 /// 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) int gtpu::create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in)
{ {
gtpu_tunnel* rx_tun = tunnels.find_tunnel(rx_teid_in); const gtpu_tunnel* rx_tun = tunnels.find_tunnel(rx_teid_in);
gtpu_tunnel* tx_tun = tunnels.find_tunnel(tx_teid_in); const gtpu_tunnel* tx_tun = tunnels.find_tunnel(tx_teid_in);
if (rx_tun == nullptr or tx_tun == nullptr) { 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); logger.error("Failed to create forwarding tunnel between teids 0x%x and 0x%x", rx_teid_in, tx_teid_in);
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
rx_tun->fwd_teid_in_present = true; tunnels.setup_forwarding(rx_teid_in, tx_teid_in);
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);
// Get all buffered PDCP PDUs, and forward them through tx tunnel // Get all buffered PDCP PDUs, and forward them through tx tunnel
std::map<uint32_t, srsran::unique_byte_buffer_t> pdus = pdcp->get_buffered_pdus(rx_tun->rnti, rx_tun->lcid); std::map<uint32_t, srsran::unique_byte_buffer_t> pdus = pdcp->get_buffered_pdus(rx_tun->rnti, rx_tun->lcid);
for (auto& pdu_pair : pdus) { for (auto& pdu_pair : pdus) {
log_message(*tx_tun, false, srsran::make_span(pdu_pair.second), pdu_pair.first); uint32_t pdcp_sn = 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), pdcp_sn);
send_pdu_to_tunnel(*tx_tun, std::move(pdu_pair.second), pdcp_sn);
} }
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
@ -718,16 +746,22 @@ void gtpu::log_message(const gtpu_tunnel& tun, bool is_rx, srsran::span<uint8_t>
if (is_rx) { if (is_rx) {
dir = "Rx"; dir = "Rx";
fmt::format_to(strbuf2, "{}:0x{:0x} > ", srsran::to_c_str(addrbuf), tun.teid_in); fmt::format_to(strbuf2, "{}:0x{:0x} > ", srsran::to_c_str(addrbuf), tun.teid_in);
if (not tunnels.get_tunnel_dl_state(tun.teid_in).value()) { switch (tun.state) {
fmt::format_to(strbuf2, "DL (buffered), "); case gtpu_tunnel_manager::tunnel_state::buffering:
} else if (tun.fwd_teid_in_present) { fmt::format_to(strbuf2, "DL (buffered), ");
const gtpu_tunnel* tx_tun = tunnels.find_tunnel(tun.fwd_teid_in); break;
srsran_assert(tx_tun != nullptr, "Invalid teid=%d", tun.fwd_teid_in); case gtpu_tunnel_manager::tunnel_state::forward_to: {
addrbuf.clear(); addrbuf.clear();
srsran::gtpu_ntoa(addrbuf, htonl(tx_tun->spgw_addr)); srsran::gtpu_ntoa(addrbuf, htonl(tun.fwd_tunnel->spgw_addr));
fmt::format_to(strbuf2, "{}:0x{:0x} (forwarded), ", srsran::to_c_str(addrbuf), tx_tun->teid_in); fmt::format_to(strbuf2, "{}:0x{:0x} (forwarded), ", srsran::to_c_str(addrbuf), tun.fwd_tunnel->teid_in);
} else { break;
fmt::format_to(strbuf2, "DL, "); }
case gtpu_tunnel_manager::tunnel_state::pdcp_active:
fmt::format_to(strbuf2, "DL, ");
break;
default:
logger.error("TEID=%d found in invalid state: %d", tun.teid_in, (int)tun.state);
break;
} }
} else { } else {
if (pdcp_sn >= 0) { if (pdcp_sn >= 0) {

@ -132,23 +132,23 @@ void test_gtpu_tunnel_manager()
const char* sgw_addr_str = "127.0.0.1"; const char* sgw_addr_str = "127.0.0.1";
struct sockaddr_in sgw_sockaddr = {}; struct sockaddr_in sgw_sockaddr = {};
srsran::net_utils::set_sockaddr(&sgw_sockaddr, sgw_addr_str, GTPU_PORT); srsran::net_utils::set_sockaddr(&sgw_sockaddr, sgw_addr_str, GTPU_PORT);
uint32_t sgw_addr = ntohl(sgw_sockaddr.sin_addr.s_addr); uint32_t sgw_addr = ntohl(sgw_sockaddr.sin_addr.s_addr);
const uint32_t drb1_lcid = 3; const uint32_t drb1_lcid = 3;
srsran::task_scheduler task_sched;
gtpu_tunnel_manager tunnels; gtpu_tunnel_manager tunnels(&task_sched);
TESTASSERT(tunnels.find_tunnel(0) == nullptr); TESTASSERT(tunnels.find_tunnel(0) == nullptr);
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty()); TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty());
TESTASSERT(tunnels.find_rnti_tunnels(0x46) == nullptr); TESTASSERT(tunnels.find_rnti_tunnels(0x46) == nullptr);
TESTASSERT(not tunnels.remove_tunnel(0));
// Creation of tunnels for different users and lcids // Creation of tunnels for different users and lcids
gtpu_tunnel* tun = tunnels.add_tunnel(0x46, drb1_lcid, 5, sgw_addr); const gtpu_tunnel* tun = tunnels.add_tunnel(0x46, drb1_lcid, 5, sgw_addr);
TESTASSERT(tun != nullptr); TESTASSERT(tun != nullptr);
TESTASSERT(tunnels.find_tunnel(tun->teid_in) == tun); TESTASSERT(tunnels.find_tunnel(tun->teid_in) == tun);
gtpu_tunnel* tun2 = tunnels.add_tunnel(0x47, drb1_lcid, 6, sgw_addr); const gtpu_tunnel* tun2 = tunnels.add_tunnel(0x47, drb1_lcid, 6, sgw_addr);
TESTASSERT(tun2 != nullptr); TESTASSERT(tun2 != nullptr);
TESTASSERT(tunnels.find_tunnel(tun2->teid_in) == tun2); TESTASSERT(tunnels.find_tunnel(tun2->teid_in) == tun2);
tun2 = tunnels.add_tunnel(0x47, drb1_lcid + 1, 6, sgw_addr); tun2 = tunnels.add_tunnel(0x47, drb1_lcid + 1, 7, sgw_addr);
TESTASSERT(tun2 != nullptr); TESTASSERT(tun2 != nullptr);
TESTASSERT(tunnels.find_tunnel(tun2->teid_in) == tun2); TESTASSERT(tunnels.find_tunnel(tun2->teid_in) == tun2);
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 1); TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 1);
@ -156,21 +156,39 @@ void test_gtpu_tunnel_manager()
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x47, drb1_lcid + 1).size() == 1); TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x47, drb1_lcid + 1).size() == 1);
// TEST: Creation/Removal of indirect tunnel // TEST: Creation/Removal of indirect tunnel
gtpu_tunnel* fwd_tun = tunnels.add_tunnel(0x46, drb1_lcid, 6, sgw_addr); const gtpu_tunnel* fwd_tun = tunnels.add_tunnel(0x46, drb1_lcid, 8, sgw_addr);
TESTASSERT(fwd_tun != nullptr); TESTASSERT(fwd_tun != nullptr);
TESTASSERT(tunnels.find_tunnel(fwd_tun->teid_in) == fwd_tun); TESTASSERT(tunnels.find_tunnel(fwd_tun->teid_in) == fwd_tun);
tun->fwd_teid_in_present = true; tunnels.setup_forwarding(tun->teid_in, fwd_tun->teid_in);
tun->fwd_teid_in = fwd_tun->teid_in;
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 2); TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 2);
// Removing a tunnel also removes any associated forwarding tunnel // Removing a tunnel also clears any associated forwarding tunnel
TESTASSERT(tunnels.remove_tunnel(tun->teid_in)); TESTASSERT(tunnels.remove_tunnel(tun->teid_in));
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty()); TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty());
// TEST: Prioritization of one TEID over another
const gtpu_tunnel* before_tun = tunnels.add_tunnel(0x46, drb1_lcid, 7, sgw_addr);
const gtpu_tunnel* after_tun = tunnels.add_tunnel(0x46, drb1_lcid, 8, sgw_addr);
TESTASSERT(before_tun != nullptr and after_tun != nullptr);
tunnels.set_tunnel_priority(before_tun->teid_in, after_tun->teid_in);
for (uint32_t i = 0; i < 1000; ++i) {
TESTASSERT(before_tun->state == gtpu_tunnel_manager::tunnel_state::pdcp_active);
TESTASSERT(after_tun->state == gtpu_tunnel_manager::tunnel_state::buffering);
// while Rx packets are received, active forwarding TEID should not be removed
tunnels.handle_rx_pdcp_sdu(before_tun->teid_in);
}
// Removing active TEID, will automatically switch TEID paths
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 2);
tunnels.remove_tunnel(before_tun->teid_in);
TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 1);
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 };
int test_gtpu_direct_tunneling(tunnel_test_event event) int test_gtpu_direct_tunneling(tunnel_test_event event)
{ {
srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST");
logger.info("\n\n**** Test GTPU Direct Tunneling ****\n");
uint16_t rnti = 0x46, rnti2 = 0x50; uint16_t rnti = 0x46, rnti2 = 0x50;
uint32_t drb1 = 3; uint32_t drb1 = 3;
uint32_t sgw_teidout1 = 1, sgw_teidout2 = 2; uint32_t sgw_teidout1 = 1, sgw_teidout2 = 2;

Loading…
Cancel
Save