ue,stack: refactor handling of radio bearears in UE stack

this is a rather large commit that is hard to split because
it touches quite a few components.

It's a preparation patch for adding NR split bearers in the next
step.

We realized that managing RLC and PDCP bearers for both NR and LTE
in the same entity doesn't work. This is because we use the LCID
as a key for all accesses. With NR dual connectivity however we
can have the same LCID active at the same time for both LTE and NR
carriers.

The patch solves that by creating a dedicated NR instance for RLC/PDCP
in the stack. But then the question arises for UL traffic on, e.g. LCID 4
what PDCP instance the GW should use for pushing SDUs. It doesnt' know
that. And in fact it doesn't need to. It just needs to know EPS
bearer IDs. So the next change was to remove the knowledge of what
LCIDs are from the GW. Make is agnostic and only work on EPS bearer IDs.

The handling and mapping between EPS bearer IDs and LCIDs for LTE
or NR (mainly PDCP for pushing data) is done in the Stack because
it has access to both.

The NAS also has a EPS bearer map but only knows about default and
dedicated bearers. It doesn't know on which logical channels they
are transmitted.
This commit is contained in:
Andre Puschmann 2021-06-14 13:23:56 +02:00
parent a4b2a065ff
commit 483a216bd5
34 changed files with 449 additions and 243 deletions

View File

@ -21,14 +21,24 @@ namespace srsue {
class gw_interface_nas
{
public:
virtual int setup_if_addr(uint32_t eps_bearer_id,
uint32_t lcid,
uint8_t pdn_type,
uint32_t ip_addr,
uint8_t* ipv6_if_id,
char* err_str) = 0;
/**
* Informs GW about new EPS default bearer being created after attach accept
* along with the assigned IP address.
*/
virtual int
setup_if_addr(uint32_t eps_bearer_id, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str) = 0;
/**
* Inform GW about the deactivation of a EPS bearer, e.g. during
* detach
*/
virtual int deactivate_eps_bearer(const uint32_t eps_bearer_id) = 0;
/**
* Informs GW about new traffic flow templates and their associated EPS bearer ID
* All TFT are applied to a dedicated EPS bearer that has a linked default bearer
*/
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 {
@ -50,7 +60,6 @@ class gw_interface_rrc
{
public:
virtual void add_mch_port(uint32_t lcid, uint32_t port) = 0;
virtual int update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid) = 0;
virtual bool is_running() = 0;
};

View File

@ -28,7 +28,9 @@ namespace srsue {
class stack_interface_rrc
{
public:
virtual srsran::tti_point get_current_tti() = 0;
virtual srsran::tti_point get_current_tti() = 0;
virtual void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) = 0;
virtual void remove_eps_bearer(uint8_t eps_bearer_id) = 0;
};
// Combined interface for PHY to access stack (MAC and RRC)

View File

@ -25,7 +25,7 @@ public:
virtual void reestablish(uint32_t lcid) = 0;
virtual void reset() = 0;
virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu, int sn = -1) = 0;
virtual void add_bearer(uint32_t lcid, srsran::pdcp_config_t cnfg) = 0;
virtual int add_bearer(uint32_t lcid, srsran::pdcp_config_t cnfg) = 0;
virtual void del_bearer(uint32_t lcid) = 0;
virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0;
virtual void config_security(uint32_t lcid, const srsran::as_security_config_t& sec_cfg) = 0;
@ -50,19 +50,23 @@ public:
virtual void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn) = 0;
};
class pdcp_interface_gw
// Data-plane interface for Stack after EPS bearer to LCID conversion
class pdcp_interface_stack
{
public:
virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0;
virtual bool is_lcid_enabled(uint32_t lcid) = 0;
virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu, int sn = -1) = 0;
virtual bool is_lcid_enabled(uint32_t lcid) = 0;
};
// STACK interface for GW
class stack_interface_gw : public pdcp_interface_gw
// STACK interface for GW (based on EPS-bearer IDs)
class stack_interface_gw
{
public:
virtual bool is_registered() = 0;
virtual bool start_service_request() = 0;
virtual void write_sdu(uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu) = 0;
///< Allow GW to query if a radio bearer for a given EPS bearer ID is currently active
virtual bool has_active_radio_bearer(uint32_t eps_bearer_id) = 0;
};
} // namespace srsue

View File

@ -72,7 +72,6 @@ public:
virtual bool is_connected() = 0;
virtual void paging_completed(bool outcome) = 0;
virtual const char* get_rb_name(uint32_t lcid) = 0;
virtual uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) = 0;
virtual bool has_nr_dc() = 0;
};

View File

@ -27,6 +27,8 @@ public:
stack_test_dummy() {}
srsran::tti_point get_current_tti() override { return srsran::tti_point{tti % 10240}; }
void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) final{};
void remove_eps_bearer(uint8_t eps_bearer_id) final{};
// Testing utility functions
void run_tti()

View File

@ -26,14 +26,10 @@ class pdcp : public srsue::pdcp_interface_rlc, public srsue::pdcp_interface_rrc
public:
pdcp(srsran::task_sched_handle task_sched_, const char* logname);
virtual ~pdcp();
void init(srsue::rlc_interface_pdcp* rlc_,
srsue::rrc_interface_pdcp* rrc_,
srsue::rrc_interface_pdcp* rrc_nr_,
srsue::gw_interface_pdcp* gw_);
void init(srsue::rlc_interface_pdcp* rlc_, srsue::rrc_interface_pdcp* rrc_, srsue::gw_interface_pdcp* gw_);
void stop();
// GW interface
// Stack interface
bool is_lcid_enabled(uint32_t lcid);
// RRC interface
@ -42,7 +38,7 @@ public:
void reset() override;
void write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, int sn = -1) override;
void write_sdu_mch(uint32_t lcid, unique_byte_buffer_t sdu);
void add_bearer(uint32_t lcid, pdcp_config_t cnfg) override;
int add_bearer(uint32_t lcid, pdcp_config_t cnfg) override;
void add_bearer_mrb(uint32_t lcid, pdcp_config_t cnfg);
void del_bearer(uint32_t lcid) override;
void change_lcid(uint32_t old_lcid, uint32_t new_lcid) override;
@ -75,7 +71,6 @@ public:
private:
srsue::rlc_interface_pdcp* rlc = nullptr;
srsue::rrc_interface_pdcp* rrc = nullptr;
srsue::rrc_interface_pdcp* rrc_nr = nullptr;
srsue::gw_interface_pdcp* gw = nullptr;
srsran::task_sched_handle task_sched;
srslog::basic_logger& logger;
@ -95,4 +90,5 @@ private:
};
} // namespace srsran
#endif // SRSRAN_PDCP_H

View File

@ -30,15 +30,6 @@ pdcp::~pdcp()
pdcp_array_mrb.clear();
}
void pdcp::init(srsue::rlc_interface_pdcp* rlc_,
srsue::rrc_interface_pdcp* rrc_,
srsue::rrc_interface_pdcp* rrc_nr_,
srsue::gw_interface_pdcp* gw_)
{
init(rlc_, rrc_, gw_);
rrc_nr = rrc_nr_;
}
void pdcp::init(srsue::rlc_interface_pdcp* rlc_, srsue::rrc_interface_pdcp* rrc_, srsue::gw_interface_pdcp* gw_)
{
rlc = rlc_;
@ -88,7 +79,7 @@ void pdcp::write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, int sn)
if (valid_lcid(lcid)) {
pdcp_array.at(lcid)->write_sdu(std::move(sdu), sn);
} else {
logger.warning("Writing sdu: lcid=%d. Deallocating sdu", lcid);
logger.warning("LCID %d doesn't exist. Deallocating SDU", lcid);
}
}
@ -99,39 +90,40 @@ void pdcp::write_sdu_mch(uint32_t lcid, unique_byte_buffer_t sdu)
}
}
void pdcp::add_bearer(uint32_t lcid, pdcp_config_t cfg)
int pdcp::add_bearer(uint32_t lcid, pdcp_config_t cfg)
{
if (not valid_lcid(lcid)) {
std::unique_ptr<pdcp_entity_base> entity;
// For now we create an pdcp entity lte for nr due to it's maturity
if (cfg.rat == srsran::srsran_rat_t::lte) {
entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid});
} else if (cfg.rat == srsran::srsran_rat_t::nr) {
if (rrc_nr == nullptr) {
logger.warning("Cannot add PDCP entity - missing rrc_nr parent pointer");
return;
}
entity.reset(new pdcp_entity_lte{rlc, rrc_nr, gw, task_sched, logger, lcid});
}
if (not entity->configure(cfg)) {
logger.error("Can not configure PDCP entity");
return;
}
if (not pdcp_array.insert(std::make_pair(lcid, std::move(entity))).second) {
logger.error("Error inserting PDCP entity in to array.");
return;
}
logger.info(
"Add %s (lcid=%d, bearer_id=%d, sn_len=%dbits)", rrc->get_rb_name(lcid), lcid, cfg.bearer_id, cfg.sn_len);
{
std::lock_guard<std::mutex> lock(cache_mutex);
valid_lcids_cached.insert(lcid);
}
} else {
logger.info("Bearer %s already configured.", rrc->get_rb_name(lcid));
if (valid_lcid(lcid)) {
logger.error("Bearer %s already configured.", rrc->get_rb_name(lcid));
return SRSRAN_ERROR;
}
std::unique_ptr<pdcp_entity_base> entity;
// For now we create an pdcp entity lte for nr due to it's maturity
if (cfg.rat == srsran::srsran_rat_t::lte) {
entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid});
} else if (cfg.rat == srsran::srsran_rat_t::nr) {
entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid});
}
if (not entity->configure(cfg)) {
logger.error("Can not configure PDCP entity");
return SRSRAN_ERROR;
}
if (not pdcp_array.insert(std::make_pair(lcid, std::move(entity))).second) {
logger.error("Error inserting PDCP entity in to array.");
return SRSRAN_ERROR;
}
{
std::lock_guard<std::mutex> lock(cache_mutex);
valid_lcids_cached.insert(lcid);
}
logger.info("Add %s (lcid=%d, bearer_id=%d, sn_len=%dbits)", rrc->get_rb_name(lcid), lcid, cfg.bearer_id, cfg.sn_len);
return SRSRAN_SUCCESS;
}
void pdcp::add_bearer_mrb(uint32_t lcid, pdcp_config_t cfg)

View File

@ -448,7 +448,7 @@ int rlc::add_bearer_mrb(uint32_t lcid)
std::unique_ptr<rlc_common> rlc_entity =
std::unique_ptr<rlc_common>(new rlc_um_lte(logger, lcid, pdcp, rrc, timers));
// configure and add to array
if (rlc_entity or rlc_entity->configure(rlc_config_t::mch_config()) == false) {
if (not rlc_entity or rlc_entity->configure(rlc_config_t::mch_config()) == false) {
logger.error("Error configuring RLC entity.");
return SRSRAN_ERROR;
}

View File

@ -64,7 +64,7 @@ public:
// Temporary GW interface
void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu);
bool is_lcid_enabled(uint32_t lcid);
bool has_active_radio_bearer(uint32_t eps_bearer_id);
bool switch_on();
void run_tti(uint32_t tti);

View File

@ -86,7 +86,6 @@ int gnb_stack_nr::init(const srsenb::stack_args_t& args_, const rrc_nr_cfg_t& rr
m_gw->init(args.coreless.gw_args, this);
char* err_str = nullptr;
if (m_gw->setup_if_addr(5,
args.coreless.drb_lcid,
LIBLTE_MME_PDN_TYPE_IPV4,
htonl(inet_addr(args.coreless.ip_addr.c_str())),
nullptr,
@ -179,9 +178,9 @@ void gnb_stack_nr::write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu)
m_pdcp->write_sdu(args.coreless.rnti, lcid, std::move(sdu));
}
bool gnb_stack_nr::is_lcid_enabled(uint32_t lcid)
bool gnb_stack_nr::has_active_radio_bearer(uint32_t eps_bearer_id)
{
return (lcid == args.coreless.drb_lcid);
return (eps_bearer_id == args.coreless.drb_lcid);
}
} // namespace srsenb

View File

@ -0,0 +1,70 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSUE_BEARER_MANAGER_H
#define SRSUE_BEARER_MANAGER_H
#include "srsran/common/common.h"
#include "srsran/common/rwlock_guard.h"
#include "srsran/srslog/srslog.h"
#include <map>
#include <stdint.h>
namespace srsue {
/**
* @brief Helper class to manage the mapping between EPS bearer and radio bearer
*
* The class maps EPS bearers that are known to NAS and GW and radio bearer (RB) that
* are only known to RRC. Since the lifetime of a EPS bearer is usually longer
* than the lifetime of a RB, the GW needs to query the Stack to check whether a
* given EPS bearer is active, i.e. a DRB is established, or not.
*
* The class also maps between RATs since each LCID can exist on either EUTRA or NR RATs, or both.
*
* Since the access of this class is happening from two different threads (GW+RRC/Stack)
* it's public interface is protected.
*
*/
class bearer_manager
{
public:
bearer_manager();
~bearer_manager();
// RRC interface
/// Registers EPS bearer with PDCP RAT type and LCID
void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid);
/// EPS bearer is removed from map when the associated DRB is deleted (e.g. after connection release)
void remove_eps_bearer(uint8_t eps_bearer_id);
// GW interface
bool has_active_radio_bearer(uint32_t eps_bearer_id);
// Stack interface to retrieve active RB
struct radio_bearer_t {
srsran::srsran_rat_t rat;
uint32_t lcid;
};
radio_bearer_t& get_radio_bearer(uint32_t eps_bearer_id);
private:
pthread_rwlock_t rwlock = {}; /// RW lock to protect access from RRC/GW threads
srslog::basic_logger& logger;
std::map<uint32_t, radio_bearer_t> eps_rb_map;
radio_bearer_t invalid_rb = {srsran::srsran_rat_t::nulltype, 0};
};
} // namespace srsue
#endif // SRSUE_BEARER_MANAGER_H

View File

@ -193,8 +193,6 @@ private:
srsran::s_tmsi_t ue_identity;
bool ue_identity_configured = false;
bool drb_up = false;
// PHY controller state machine
std::unique_ptr<phy_controller> phy_ctrl;
@ -382,6 +380,7 @@ private:
void release_drb(uint32_t drb_id);
uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id);
uint32_t get_drb_id_for_eps_bearer(const uint32_t& eps_bearer_id);
uint32_t get_eps_bearer_id_for_drb_id(const uint32_t& drb_id);
void add_mrb(uint32_t lcid, uint32_t port);
// Helpers for setting default values

View File

@ -20,6 +20,7 @@
#include "srsran/common/common_nr.h"
#include "srsran/common/stack_procedure.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/interfaces/ue_interfaces.h"
#include "srsran/interfaces/ue_nr_interfaces.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
#include "srsue/hdr/stack/upper/gw.h"
@ -29,7 +30,6 @@ namespace srsue {
class usim_interface_rrc_nr;
class pdcp_interface_rrc;
class rlc_interface_rrc;
class stack_interface_rrc;
// Expert arguments to create GW without proper RRC
struct core_less_args_t {

View File

@ -31,6 +31,7 @@
#include "upper/nas.h"
#include "upper/usim.h"
#include "bearer_manager.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/multiqueue.h"
#include "srsran/common/string_helpers.h"
@ -159,12 +160,13 @@ public:
}
// Interface for GW
void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) final;
bool is_lcid_enabled(uint32_t lcid) final { return pdcp.is_lcid_enabled(lcid); }
void write_sdu(uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu) final;
bool has_active_radio_bearer(uint32_t eps_bearer_id) final;
// Interface for RRC
tti_point get_current_tti() final { return current_tti; }
void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) final;
void remove_eps_bearer(uint8_t eps_bearer_id) final;
srsran::ext_task_sched_handle get_task_sched() { return {&task_sched}; }
@ -228,6 +230,8 @@ private:
srsue::nas nas;
std::unique_ptr<usim_base> usim;
bearer_manager bearers; // helper to manage mapping between EPS and radio bearers
// Metrics helper
uint32_t ul_dropped_sdus = 0;
};

View File

@ -102,11 +102,13 @@ public:
}
// Interface for GW
void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) final;
bool is_lcid_enabled(uint32_t lcid) final { return pdcp->is_lcid_enabled(lcid); }
void write_sdu(uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu) final;
bool has_active_radio_bearer(uint32_t eps_bearer_id) final { return true; /* TODO: add EPS to LCID mapping */ }
// Interface for RRC
srsran::tti_point get_current_tti() { return srsran::tti_point{0}; };
void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) final{};
void remove_eps_bearer(uint8_t eps_bearer_id) final{};
private:
void run_thread() final;

View File

@ -21,6 +21,7 @@
#include "srsran/interfaces/ue_gw_interfaces.h"
#include "srsran/srslog/srslog.h"
#include "tft_packet_filter.h"
#include <atomic>
#include <net/if.h>
#include <netinet/in.h>
@ -53,19 +54,17 @@ public:
// NAS interface
int setup_if_addr(uint32_t eps_bearer_id,
uint32_t lcid,
uint8_t pdn_type,
uint32_t ip_addr,
uint8_t* ipv6_if_addr,
char* err_str);
int deactivate_eps_bearer(const uint32_t eps_bearer_id);
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);
int update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid);
bool is_running();
private:
@ -75,19 +74,19 @@ private:
gw_args_t args = {};
bool running = false;
std::atomic<bool> running = {false};
bool run_enable = false;
int32_t netns_fd = 0;
int32_t tun_fd = 0;
struct ifreq ifr = {};
int32_t sock = 0;
bool if_up = false;
uint32_t default_lcid = 0;
static const int NOT_ASSIGNED = -1;
int32_t default_eps_bearer_id = NOT_ASSIGNED;
srslog::basic_logger& logger;
std::map<uint32_t, uint32_t> eps_lcid; // Mapping between eps bearer ID and LCID
uint32_t current_ip_addr = 0;
uint8_t current_if_id[8];

View File

@ -231,6 +231,9 @@ private:
void airplane_mode_sim_switch_off();
void airplane_mode_sim_switch_on();
// Misc helpers
void clear_eps_bearer();
// FSM Helpers
void enter_state(emm_state_t state_);
void enter_emm_null();

View File

@ -59,14 +59,12 @@ class tft_packet_filter_t
{
public:
tft_packet_filter_t(uint8_t eps_bearer_id_,
uint8_t lcid_,
const LIBLTE_MME_PACKET_FILTER_STRUCT& tft_,
srslog::basic_logger& logger);
bool match(const srsran::unique_byte_buffer_t& pdu);
bool filter_contains(uint16_t filtertype);
uint8_t eps_bearer_id{};
uint8_t lcid = {};
uint8_t id = {};
uint8_t eval_precedence = {};
uint32_t active_filters = {};
@ -108,15 +106,12 @@ public:
explicit tft_pdu_matcher(srslog::basic_logger& logger) : logger(logger) {}
~tft_pdu_matcher(){};
void set_default_lcid(const uint8_t lcid);
uint8_t check_tft_filter_match(const srsran::unique_byte_buffer_t& pdu);
int check_tft_filter_match(const srsran::unique_byte_buffer_t& pdu, uint8_t& eps_bearer_id);
int apply_traffic_flow_template(const uint8_t& erab_id,
const uint8_t& lcid,
const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft);
private:
srslog::basic_logger& logger;
uint8_t default_lcid = 0;
std::mutex tft_mutex;
typedef std::map<uint16_t, tft_packet_filter_t> tft_filter_map_t;
tft_filter_map_t tft_filter_map;

View File

@ -11,7 +11,7 @@ add_subdirectory(mac)
add_subdirectory(rrc)
add_subdirectory(upper)
set(SOURCES ue_stack_lte.cc)
set(SOURCES ue_stack_lte.cc bearer_manager.cc)
add_library(srsue_stack STATIC ${SOURCES})
add_subdirectory(mac_nr)

View File

@ -0,0 +1,69 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsue/hdr/stack/bearer_manager.h"
namespace srsue {
bearer_manager::bearer_manager() : logger(srslog::fetch_basic_logger("STCK", false))
{
pthread_rwlock_init(&rwlock, nullptr);
}
bearer_manager::~bearer_manager()
{
pthread_rwlock_destroy(&rwlock);
}
void bearer_manager::add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid)
{
srsran::rwlock_write_guard rw_lock(rwlock);
auto it = eps_rb_map.find(eps_bearer_id);
if (it != eps_rb_map.end()) {
logger.error("EPS bearer ID %d already registered", eps_bearer_id);
return;
}
eps_rb_map.emplace(eps_bearer_id, radio_bearer_t{rat, lcid});
logger.info("Registered EPS bearer ID %d for lcid=%d over %s-PDCP", eps_bearer_id, lcid, to_string(rat).c_str());
}
/// EPS bearer is removed from map when the associated DRB is deleted (e.g. after connection release)
void bearer_manager::remove_eps_bearer(uint8_t eps_bearer_id)
{
srsran::rwlock_write_guard rw_lock(rwlock);
auto it = eps_rb_map.find(eps_bearer_id);
if (it == eps_rb_map.end()) {
logger.error("Can't remove EPS bearer ID %d", eps_bearer_id);
return;
}
eps_rb_map.erase(it);
logger.info("Removed mapping for EPS bearer ID %d", eps_bearer_id);
}
// GW interface
bool bearer_manager::has_active_radio_bearer(uint32_t eps_bearer_id)
{
srsran::rwlock_read_guard rw_lock(rwlock);
return eps_rb_map.find(eps_bearer_id) != eps_rb_map.end();
}
// Stack interface
bearer_manager::radio_bearer_t& bearer_manager::get_radio_bearer(uint32_t eps_bearer_id)
{
srsran::rwlock_read_guard rw_lock(rwlock);
if (eps_rb_map.find(eps_bearer_id) != eps_rb_map.end()) {
return eps_rb_map.at(eps_bearer_id);
}
return invalid_rb;
}
} // namespace srsue

View File

@ -53,7 +53,6 @@ rrc::rrc(stack_interface_rrc* stack_, srsran::task_sched_handle task_sched_) :
task_sched(task_sched_),
state(RRC_STATE_IDLE),
last_state(RRC_STATE_CONNECTED),
drb_up(false),
logger(srslog::fetch_basic_logger("RRC")),
measurements(new rrc_meas()),
cell_searcher(this),
@ -191,11 +190,6 @@ bool rrc::is_connected()
return (RRC_STATE_CONNECTED == state);
}
bool rrc::have_drb()
{
return drb_up;
}
/*
*
* RRC State Machine
@ -1113,7 +1107,6 @@ void rrc::leave_connected()
srsran::console("RRC IDLE\n");
logger.info("Leaving RRC_CONNECTED state");
state = RRC_STATE_IDLE;
drb_up = false;
security_is_activated = false;
// 1> reset MAC;
@ -2669,19 +2662,24 @@ void rrc::add_drb(const drb_to_add_mod_s& drb_cnfg)
}
mac->setup_lcid(lcid, log_chan_group, priority, prioritized_bit_rate, bucket_size_duration);
drbs[drb_cnfg.drb_id] = drb_cnfg;
drb_up = true;
logger.info("Added DRB Id %d (LCID=%d)", drb_cnfg.drb_id, lcid);
// Update LCID if gw is running
if (gw->is_running()) {
gw->update_lcid(drb_cnfg.eps_bearer_id, lcid);
uint8_t eps_bearer_id = 5; // default?
if (drb_cnfg.eps_bearer_id_present) {
eps_bearer_id = drb_cnfg.eps_bearer_id;
}
// register EPS bearer over LTE PDCP
stack->add_eps_bearer(eps_bearer_id, srsran::srsran_rat_t::lte, lcid);
drbs[drb_cnfg.drb_id] = drb_cnfg;
logger.info("Added DRB Id %d (LCID=%d)", drb_cnfg.drb_id, lcid);
}
void rrc::release_drb(uint32_t drb_id)
{
if (drbs.find(drb_id) != drbs.end()) {
logger.info("Releasing DRB Id %d", drb_id);
// remove EPS bearer associated with this DRB from Stack (GW will trigger service request if needed)
stack->remove_eps_bearer(get_eps_bearer_id_for_drb_id(drb_id));
drbs.erase(drb_id);
} else {
logger.error("Couldn't release DRB Id %d. Doesn't exist.", drb_id);
@ -2704,6 +2702,17 @@ uint32_t rrc::get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id)
return lcid;
}
uint32_t rrc::get_eps_bearer_id_for_drb_id(const uint32_t& drb_id)
{
// check if this bearer id exists and return it's LCID
for (auto& drb : drbs) {
if (drb.first == drb_id) {
return drb.second.eps_bearer_id;
}
}
return 0;
}
uint32_t rrc::get_drb_id_for_eps_bearer(const uint32_t& eps_bearer_id)
{
// check if this bearer id exists and return it's LCID

View File

@ -1362,7 +1362,10 @@ bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg)
srsran::pdcp_config_t pdcp_cfg = make_drb_pdcp_config_t(drb_cfg.drb_id, true, drb_cfg.pdcp_cfg);
pdcp->add_bearer(lcid, pdcp_cfg);
gw->update_lcid(eps_bearer_id, lcid);
// register EPS bearer over NR PDCP
stack->add_eps_bearer(eps_bearer_id, srsran::srsran_rat_t::nr, lcid);
return true;
}

View File

@ -66,7 +66,7 @@ class dummy_pdcp : public pdcp_interface_rrc
void reestablish(uint32_t lcid){};
void reset(){};
void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu, int sn = -1){};
void add_bearer(uint32_t lcid, srsran::pdcp_config_t cnfg){};
int add_bearer(uint32_t lcid, srsran::pdcp_config_t cnfg) { return SRSRAN_SUCCESS; };
void del_bearer(uint32_t lcid){};
void change_lcid(uint32_t old_lcid, uint32_t new_lcid){};
void config_security(uint32_t lcid, const srsran::as_security_config_t& sec_cfg){};
@ -81,7 +81,6 @@ class dummy_pdcp : public pdcp_interface_rrc
class dummy_gw : public gw_interface_rrc
{
void add_mch_port(uint32_t lcid, uint32_t port){};
int update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid) { return SRSRAN_SUCCESS; };
bool is_running() { return true; };
};
@ -102,6 +101,8 @@ class dummy_sim : public usim_interface_rrc_nr
class dummy_stack : public stack_interface_rrc
{
srsran::tti_point get_current_tti() { return srsran::tti_point(); };
void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid){};
void remove_eps_bearer(uint8_t eps_bearer_id){};
};
int rrc_nr_cap_request_test()

View File

@ -214,10 +214,11 @@ int ue_stack_lte::init(const stack_args_t& args_)
nas.init(usim.get(), &rrc, gw, args.nas);
mac_nr_args_t mac_nr_args = {};
mac_nr.init(mac_nr_args, phy_nr, &rlc, &rrc_nr);
mac_nr.init(mac_nr_args, phy_nr, &rlc_nr, &rrc_nr);
rlc_nr.init(&pdcp_nr, &rrc_nr, task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */);
pdcp_nr.init(&rlc_nr, &rrc_nr, gw);
rrc_nr.init(phy_nr, &mac_nr, &rlc, &pdcp, gw, &rrc, usim.get(), task_sched.get_timer_handler(), nullptr, args.rrc_nr);
rrc_nr.init(
phy_nr, &mac_nr, &rlc_nr, &pdcp_nr, gw, &rrc, usim.get(), task_sched.get_timer_handler(), this, args.rrc_nr);
rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc);
running = true;
@ -338,26 +339,58 @@ void ue_stack_lte::run_thread()
* Stack Interfaces
**********************************************************************************************************************/
/********************
* RRC Interface
*******************/
void ue_stack_lte::add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid)
{
bearers.add_eps_bearer(eps_bearer_id, rat, lcid);
}
void ue_stack_lte::remove_eps_bearer(uint8_t eps_bearer_id)
{
bearers.remove_eps_bearer(eps_bearer_id);
}
/********************
* GW Interface
*******************/
/**
* Push GW SDU to stack
* @param lcid
* GW calls write_sdu() to push SDU for EPS bearer to stack.
* If the EPS bearer ID is valid it will deliver the PDU to the
* registered PDCP entity.
*
* @param eps_bearer_id
* @param sdu
* @param blocking
*/
void ue_stack_lte::write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu)
void ue_stack_lte::write_sdu(uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu)
{
auto task = [this, lcid](srsran::unique_byte_buffer_t& sdu) { pdcp.write_sdu(lcid, std::move(sdu)); };
bool ret = gw_queue_id.try_push(std::bind(task, std::move(sdu))).has_value();
auto bearer = bearers.get_radio_bearer(eps_bearer_id);
auto task = [this, eps_bearer_id, bearer](srsran::unique_byte_buffer_t& sdu) {
// route SDU to PDCP entity
if (bearer.rat == srsran_rat_t::lte) {
pdcp.write_sdu(bearer.lcid, std::move(sdu));
} else if (bearer.rat == srsran_rat_t::nr) {
pdcp_nr.write_sdu(bearer.lcid, std::move(sdu));
} else {
stack_logger.warning("Can't deliver SDU for EPS bearer %d. Dropping it.", eps_bearer_id);
}
};
bool ret = gw_queue_id.try_push(std::bind(task, std::move(sdu))).has_value();
if (not ret) {
pdcp_logger.info("GW SDU with lcid=%d was discarded.", lcid);
pdcp_logger.info("GW SDU with lcid=%d was discarded.", bearer.lcid);
ul_dropped_sdus++;
}
}
bool ue_stack_lte::has_active_radio_bearer(uint32_t eps_bearer_id)
{
return bearers.has_active_radio_bearer(eps_bearer_id);
}
/**
* Check whether nas is attached
* @return bool wether NAS is in EMM_REGISTERED

View File

@ -111,7 +111,7 @@ bool ue_stack_nr::switch_on()
{
// statically setup TUN (will be done through RRC later)
char* err_str = nullptr;
if (gw->setup_if_addr(5, 4, LIBLTE_MME_PDN_TYPE_IPV4, htonl(inet_addr("192.168.1.3")), nullptr, err_str)) {
if (gw->setup_if_addr(5, LIBLTE_MME_PDN_TYPE_IPV4, htonl(inet_addr("192.168.1.3")), nullptr, err_str)) {
printf("Error configuring TUN interface\n");
}
return true;

View File

@ -166,7 +166,6 @@ void gw::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
NAS interface
*******************************************************************************/
int gw::setup_if_addr(uint32_t eps_bearer_id,
uint32_t lcid,
uint8_t pdn_type,
uint32_t ip_addr,
uint8_t* ipv6_if_addr,
@ -186,46 +185,34 @@ int gw::setup_if_addr(uint32_t eps_bearer_id,
}
}
eps_lcid[eps_bearer_id] = lcid;
default_lcid = lcid;
tft_matcher.set_default_lcid(lcid);
default_eps_bearer_id = static_cast<int>(eps_bearer_id);
// Setup a thread to receive packets from the TUN device
start(GW_THREAD_PRIO);
return SRSRAN_SUCCESS;
}
int gw::deactivate_eps_bearer(const uint32_t eps_bearer_id)
{
// only deactivation of default bearer
if (eps_bearer_id == static_cast<uint32_t>(default_eps_bearer_id)) {
logger.debug("Deactivating EPS bearer %d", eps_bearer_id);
default_eps_bearer_id = NOT_ASSIGNED;
return SRSRAN_SUCCESS;
}
logger.error("Couldn't deactivate EPS bearer %d", eps_bearer_id);
return SRSRAN_ERROR;
}
bool gw::is_running()
{
return running;
}
int gw::update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid)
{
auto it = eps_lcid.find(eps_bearer_id);
if (it != eps_lcid.end()) {
uint32_t old_lcid = eps_lcid[eps_bearer_id];
logger.debug("Found EPS bearer %d. Update old lcid %d to new lcid %d", eps_bearer_id, old_lcid, new_lcid);
eps_lcid[eps_bearer_id] = new_lcid;
if (old_lcid == default_lcid) {
logger.debug("Defaulting new lcid %d", new_lcid);
default_lcid = new_lcid;
tft_matcher.set_default_lcid(new_lcid);
}
// TODO: update need filters if not the default lcid
} else {
logger.error("Did not found EPS bearer %d for updating LCID.", eps_bearer_id);
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int gw::apply_traffic_flow_template(const uint8_t& erab_id,
const uint8_t& lcid,
const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft)
{
return tft_matcher.apply_traffic_flow_template(erab_id, lcid, tft);
return tft_matcher.apply_traffic_flow_template(erab_id, tft);
}
void gw::set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms)
@ -299,8 +286,8 @@ void gw::run_thread()
if (pkt_len == pdu->N_bytes) {
logger.info(pdu->msg, pdu->N_bytes, "TX PDU");
// Make sure UE is attached
while (run_enable && !stack->is_registered() && register_wait < REGISTER_WAIT_TOUT) {
// Make sure UE is attached and has default EPS bearer activated
while (run_enable && default_eps_bearer_id == NOT_ASSIGNED && register_wait < REGISTER_WAIT_TOUT) {
if (!register_wait) {
logger.info("UE is not attached, waiting for NAS attach (%d/%d)", register_wait, REGISTER_WAIT_TOUT);
}
@ -310,12 +297,18 @@ void gw::run_thread()
register_wait = 0;
// If we are still not attached by this stage, drop packet
if (run_enable && !stack->is_registered()) {
if (run_enable && default_eps_bearer_id == NOT_ASSIGNED) {
continue;
}
// Beyond this point we should have a activated default EPS bearer
srsran_assert(default_eps_bearer_id != NOT_ASSIGNED, "Default EPS bearer not activated");
uint8_t eps_bearer_id = default_eps_bearer_id;
tft_matcher.check_tft_filter_match(pdu, eps_bearer_id);
// Wait for service request if necessary
while (run_enable && !stack->is_lcid_enabled(default_lcid) && service_wait < SERVICE_WAIT_TOUT) {
while (run_enable && !stack->has_active_radio_bearer(eps_bearer_id) && service_wait < SERVICE_WAIT_TOUT) {
if (!service_wait) {
logger.info(
"UE does not have service, waiting for NAS service request (%d/%d)", service_wait, SERVICE_WAIT_TOUT);
@ -331,21 +324,18 @@ void gw::run_thread()
break;
}
uint8_t lcid = tft_matcher.check_tft_filter_match(pdu);
// Send PDU directly to PDCP
if (stack->is_lcid_enabled(lcid)) {
pdu->set_timestamp();
ul_tput_bytes += pdu->N_bytes;
stack->write_sdu(lcid, std::move(pdu));
do {
pdu = srsran::make_byte_buffer();
if (!pdu) {
logger.error("Fatal Error: Couldn't allocate PDU in run_thread().");
usleep(100000);
}
} while (!pdu);
idx = 0;
}
pdu->set_timestamp();
ul_tput_bytes += pdu->N_bytes;
stack->write_sdu(eps_bearer_id, std::move(pdu));
do {
pdu = srsran::make_byte_buffer();
if (!pdu) {
logger.error("Fatal Error: Couldn't allocate PDU in run_thread().");
usleep(100000);
}
} while (!pdu);
idx = 0;
} else {
idx += N_bytes;
logger.debug("Entire packet not read from socket. Total Length %d, N_Bytes %d.", ip_pkt->tot_len, pdu->N_bytes);

View File

@ -161,32 +161,38 @@ void nas::run_tti()
}
}
/*******************************************************************************
* FSM Helperse
******************************************************************************/
void nas::enter_emm_null()
// Helper method to inform GW about remove default EPS bearer
void nas::clear_eps_bearer()
{
// Deactivate EPS bearer according to Sec. 5.5.2.2.2
logger.debug("Clearing EPS bearer context");
for (const auto& bearer : eps_bearer) {
if (bearer.second.type == DEFAULT_EPS_BEARER) {
gw->deactivate_eps_bearer(bearer.second.eps_bearer_id);
}
}
eps_bearer.clear();
}
/*******************************************************************************
* FSM Helpers
******************************************************************************/
void nas::enter_emm_null()
{
clear_eps_bearer();
state.set_null();
}
void nas::enter_emm_deregistered_initiated()
{
// Deactivate EPS bearer according to Sec. 5.5.2.2.2
logger.debug("Clearing EPS bearer context");
eps_bearer.clear();
clear_eps_bearer();
state.set_deregistered_initiated();
}
void nas::enter_emm_deregistered(emm_state_t::deregistered_substate_t substate)
{
// TODO Start cell selection.
// Deactivate EPS bearer according to Sec. 5.5.2.2.2
logger.debug("Clearing EPS bearer context");
eps_bearer.clear();
clear_eps_bearer();
state.set_deregistered(substate);
}
@ -1010,6 +1016,14 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu)
liblte_mme_unpack_activate_default_eps_bearer_context_request_msg(&attach_accept.esm_msg,
&act_def_eps_bearer_context_req);
// make sure we don't already have a default EPS bearer activated
for (const auto& bearer : eps_bearer) {
if (bearer.second.type == DEFAULT_EPS_BEARER) {
logger.error("Only one default EPS bearer supported.");
return;
}
}
if ((cfg.apn_protocol == "ipv4" && LIBLTE_MME_PDN_TYPE_IPV6 == act_def_eps_bearer_context_req.pdn_addr.pdn_type) ||
(cfg.apn_protocol == "ipv6" && LIBLTE_MME_PDN_TYPE_IPV4 == act_def_eps_bearer_context_req.pdn_addr.pdn_type)) {
logger.error("Failed to attach -- Mismatch between PDN protocol and PDN type in attach accept.");
@ -1045,7 +1059,6 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu)
// Setup GW
char* err_str = nullptr;
if (gw->setup_if_addr(act_def_eps_bearer_context_req.eps_bearer_id,
rrc->get_lcid_for_eps_bearer(act_def_eps_bearer_context_req.eps_bearer_id),
LIBLTE_MME_PDN_TYPE_IPV4,
ip_addr,
nullptr,
@ -1078,7 +1091,6 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu)
// Setup GW
char* err_str = nullptr;
if (gw->setup_if_addr(act_def_eps_bearer_context_req.eps_bearer_id,
rrc->get_lcid_for_eps_bearer(act_def_eps_bearer_context_req.eps_bearer_id),
LIBLTE_MME_PDN_TYPE_IPV6,
0,
ipv6_if_id,
@ -1129,7 +1141,6 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu)
char* err_str = nullptr;
if (gw->setup_if_addr(act_def_eps_bearer_context_req.eps_bearer_id,
rrc->get_lcid_for_eps_bearer(act_def_eps_bearer_context_req.eps_bearer_id),
LIBLTE_MME_PDN_TYPE_IPV4V6,
ip_addr,
ipv6_if_id,
@ -1564,7 +1575,7 @@ void nas::parse_activate_dedicated_eps_bearer_context_request(uint32_t lcid, uni
}
// apply packet filters to GW
gw->apply_traffic_flow_template(request.eps_bearer_id, rrc->get_lcid_for_eps_bearer(request.eps_bearer_id), tft);
gw->apply_traffic_flow_template(request.eps_bearer_id, tft);
send_activate_dedicated_eps_bearer_context_accept(request.proc_transaction_id, request.eps_bearer_id);
}
@ -1593,6 +1604,9 @@ void nas::parse_deactivate_eps_bearer_context_request(unique_byte_buffer_t pdu)
eps_bearer_map_t::iterator it = eps_bearer.find(request.eps_bearer_id);
eps_bearer.erase(it);
// inform GW about removed EPS bearer
gw->deactivate_eps_bearer(request.eps_bearer_id);
logger.info("Removed EPS bearer context (eps_bearer_id=%d)", request.eps_bearer_id);
send_deactivate_eps_bearer_context_accept(request.proc_transaction_id, request.eps_bearer_id);
@ -1951,7 +1965,7 @@ void nas::send_detach_request(bool switch_off)
}
if (switch_off) {
enter_emm_deregistered_initiated();
enter_emm_deregistered(emm_state_t::deregistered_substate_t::null);
} else {
// we are expecting a response from the core
state.set_deregistered_initiated();

View File

@ -23,10 +23,10 @@ public:
bool is_registered() { return true; }
bool start_service_request() { return true; };
void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { return; }
bool is_lcid_enabled(uint32_t lcid) { return true; }
bool has_active_radio_bearer(uint32_t eps_bearer_id) { return true; }
};
int gw_change_lcid_test()
int gw_test()
{
srsue::gw_args_t gw_args;
gw_args.tun_dev_name = "tun1";
@ -43,8 +43,7 @@ int gw_change_lcid_test()
char* err_str = nullptr;
int rtn = 0;
rtn = gw.setup_if_addr(
eps_bearer_id, old_lcid, LIBLTE_MME_PDN_TYPE_IPV4, htonl(inet_addr("192.168.56.32")), nullptr, err_str);
rtn = gw.setup_if_addr(eps_bearer_id, LIBLTE_MME_PDN_TYPE_IPV4, htonl(inet_addr("192.168.56.32")), nullptr, err_str);
if (rtn != SRSRAN_SUCCESS) {
srslog::fetch_basic_logger("TEST", false)
@ -53,8 +52,8 @@ int gw_change_lcid_test()
return SRSRAN_SUCCESS;
}
TESTASSERT(gw.update_lcid(eps_bearer_id, new_lcid) == SRSRAN_SUCCESS);
TESTASSERT(gw.update_lcid(non_existing_eps_bearer_id, new_lcid) == SRSRAN_ERROR);
TESTASSERT(gw.deactivate_eps_bearer(eps_bearer_id) == SRSRAN_SUCCESS);
TESTASSERT(gw.deactivate_eps_bearer(non_existing_eps_bearer_id) == SRSRAN_ERROR);
gw.stop();
return SRSRAN_SUCCESS;
}
@ -63,7 +62,7 @@ int main(int argc, char** argv)
{
srslog::init();
TESTASSERT(gw_change_lcid_test() == SRSRAN_SUCCESS);
TESTASSERT(gw_test() == SRSRAN_SUCCESS);
return SRSRAN_SUCCESS;
}

View File

@ -63,18 +63,19 @@ using namespace srsran;
namespace srsran {
// fake classes
class pdcp_dummy : public rrc_interface_pdcp, public pdcp_interface_gw
class pdcp_dummy : public rrc_interface_pdcp, public pdcp_interface_stack
{
public:
void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) {}
void write_pdu_bcch_bch(unique_byte_buffer_t pdu) {}
void write_pdu_bcch_dlsch(unique_byte_buffer_t pdu) {}
void write_pdu_pcch(unique_byte_buffer_t pdu) {}
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {}
const char* get_rb_name(uint32_t lcid) { return "lcid"; }
void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {}
bool is_lcid_enabled(uint32_t lcid) { return false; }
void notify_pdcp_integrity_error(uint32_t lcid) {}
void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) override {}
void write_pdu_bcch_bch(unique_byte_buffer_t pdu) override {}
void write_pdu_bcch_dlsch(unique_byte_buffer_t pdu) override {}
void write_pdu_pcch(unique_byte_buffer_t pdu) override {}
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) override {}
const char* get_rb_name(uint32_t lcid) override { return "lcid"; }
void write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, int sn = -1) override {}
bool is_eps_bearer_id_enabled(uint32_t eps_bearer_id) { return false; }
void notify_pdcp_integrity_error(uint32_t lcid) override {}
bool is_lcid_enabled(uint32_t lcid) override { return false; }
};
class rrc_dummy : public rrc_interface_nas
@ -131,7 +132,7 @@ private:
class test_stack_dummy : public srsue::stack_test_dummy, public stack_interface_gw, public thread
{
public:
test_stack_dummy(pdcp_interface_gw* pdcp_) : pdcp(pdcp_), thread("DUMMY STACK") {}
test_stack_dummy(pdcp_interface_stack* pdcp_) : pdcp(pdcp_), thread("DUMMY STACK") {}
void init(srsue::nas* nas_)
{
nas = nas_;
@ -139,7 +140,7 @@ public:
}
bool switch_on() { return true; }
void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { pdcp->write_sdu(lcid, std::move(sdu)); }
bool is_lcid_enabled(uint32_t lcid) { return pdcp->is_lcid_enabled(lcid); }
bool has_active_radio_bearer(uint32_t eps_bearer_id) { return true; }
bool is_registered() { return true; }
@ -162,7 +163,7 @@ public:
running = false;
wait_thread_finish();
}
pdcp_interface_gw* pdcp = nullptr;
pdcp_interface_stack* pdcp = nullptr;
srsue::nas* nas = nullptr;
std::atomic<bool> running = {false};
};
@ -170,7 +171,6 @@ public:
class gw_dummy : public gw_interface_nas, public gw_interface_pdcp
{
int setup_if_addr(uint32_t eps_bearer_id,
uint32_t lcid,
uint8_t pdn_type,
uint32_t ip_addr,
uint8_t* ipv6_if_id,
@ -178,8 +178,8 @@ class gw_dummy : public gw_interface_nas, public gw_interface_pdcp
{
return SRSRAN_SUCCESS;
}
int deactivate_eps_bearer(const uint32_t eps_bearer_id) { return SRSRAN_SUCCESS; }
int apply_traffic_flow_template(const uint8_t& eps_bearer_id,
const uint8_t& lcid,
const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft)
{
return SRSRAN_SUCCESS;

View File

@ -99,7 +99,6 @@ uint8_t ipv6_unmatched_packet_lport[] = {
0x80, 0x61, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
#define EPS_BEARER_ID 6
#define LCID 1
int tft_filter_test_ipv6_combined()
{
@ -150,7 +149,7 @@ int tft_filter_test_ipv6_combined()
packet_filter.filter_size = sizeof(ipv6_filter);
memcpy(packet_filter.filter, ipv6_filter, sizeof(ipv6_filter));
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger);
// Check filter
TESTASSERT(filter.match(ip_msg1));
@ -199,7 +198,7 @@ int tft_filter_test_single_local_port()
packet_filter.filter_size = 3;
memcpy(packet_filter.filter, filter_message, 3);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger);
// Check filter
TESTASSERT(filter.match(ip_msg1));
@ -245,7 +244,7 @@ int tft_filter_test_single_remote_port()
packet_filter.filter_size = 3;
memcpy(packet_filter.filter, filter_message, 3);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger);
// Check filter
TESTASSERT(filter.match(ip_msg1));
@ -294,7 +293,7 @@ int tft_filter_test_ipv4_local_addr()
packet_filter.filter_size = filter_size;
memcpy(packet_filter.filter, filter_message, filter_size);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger);
// Check filter
TESTASSERT(filter.match(ip_msg1));
@ -342,7 +341,7 @@ int tft_filter_test_ipv4_remote_addr()
packet_filter.filter_size = filter_size;
memcpy(packet_filter.filter, filter_message, filter_size);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger);
// Check filter
TESTASSERT(filter.match(ip_msg1));
@ -391,7 +390,7 @@ int tft_filter_test_ipv4_tos()
packet_filter.filter_size = filter_size;
memcpy(packet_filter.filter, filter_message, filter_size);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger);
srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger);
// Check filter
TESTASSERT(filter.match(ip_msg1));

View File

@ -24,11 +24,9 @@ extern "C" {
namespace srsue {
tft_packet_filter_t::tft_packet_filter_t(uint8_t eps_bearer_id_,
uint8_t lcid_,
const LIBLTE_MME_PACKET_FILTER_STRUCT& tft,
srslog::basic_logger& logger) :
eps_bearer_id(eps_bearer_id_),
lcid(lcid_),
id(tft.id),
eval_precedence(tft.eval_precedence),
active_filters(0),
@ -380,23 +378,28 @@ bool tft_packet_filter_t::match_port(const srsran::unique_byte_buffer_t& pdu)
return true;
}
uint8_t tft_pdu_matcher::check_tft_filter_match(const srsran::unique_byte_buffer_t& pdu)
/**
* Checks whether the provided PDU matches any configured TFT.
* If it finds a match, it updates the eps_bearer_id parameter.
* @param pdu Reference to the PDU to check.
* @param eps_bearer_id Reference to variable to store EPS bearer ID.
* @return SRSRAN_SUCCESS if a reference could be found, SRSRAN_ERROR otherwise.
*/
int tft_pdu_matcher::check_tft_filter_match(const srsran::unique_byte_buffer_t& pdu, uint8_t& eps_bearer_id)
{
std::lock_guard<std::mutex> lock(tft_mutex);
uint8_t lcid = default_lcid;
for (std::pair<const uint16_t, tft_packet_filter_t>& filter_pair : tft_filter_map) {
bool match = filter_pair.second.match(pdu);
if (match) {
lcid = filter_pair.second.lcid;
logger.debug("Found filter match -- EPS bearer Id %d, LCID %d", filter_pair.second.eps_bearer_id, lcid);
break;
eps_bearer_id = filter_pair.second.eps_bearer_id;
logger.debug("Found filter match -- EPS bearer Id %d", filter_pair.second.eps_bearer_id);
return SRSRAN_SUCCESS;
}
}
return lcid;
return SRSRAN_ERROR;
}
int tft_pdu_matcher::apply_traffic_flow_template(const uint8_t& erab_id,
const uint8_t& lcid,
const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft)
{
std::lock_guard<std::mutex> lock(tft_mutex);
@ -404,7 +407,7 @@ int tft_pdu_matcher::apply_traffic_flow_template(const uint8_t&
case LIBLTE_MME_TFT_OPERATION_CODE_CREATE_NEW_TFT:
for (int i = 0; i < tft->packet_filter_list_size; i++) {
logger.info("New packet filter for TFT");
tft_packet_filter_t filter(erab_id, lcid, tft->packet_filter_list[i], logger);
tft_packet_filter_t filter(erab_id, tft->packet_filter_list[i], logger);
auto it = tft_filter_map.insert(std::make_pair(filter.eval_precedence, filter));
if (it.second == false) {
logger.error("Error inserting TFT Packet Filter");
@ -419,9 +422,4 @@ int tft_pdu_matcher::apply_traffic_flow_template(const uint8_t&
return SRSRAN_SUCCESS;
}
void tft_pdu_matcher::set_default_lcid(const uint8_t lcid)
{
default_lcid = lcid;
}
} // namespace srsue

View File

@ -53,16 +53,15 @@ public:
void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu);
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu);
int setup_if_addr(uint32_t eps_bearer_id,
uint32_t lcid,
uint8_t pdn_type,
uint32_t ip_addr,
uint8_t* ipv6_if_id,
char* err_str);
int update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid);
bool is_running();
int deactivate_eps_bearer(const uint32_t eps_bearer_id);
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_ = 0);
@ -71,7 +70,7 @@ public:
void send_queued_data();
void loop_back_pdu_with_tft(uint32_t input_lcid, srsran::unique_byte_buffer_t pdu);
void loop_back_pdu_with_tft(srsran::unique_byte_buffer_t pdu);
private:
std::unique_ptr<lte_ttcn3_phy> phy;
@ -82,9 +81,11 @@ private:
test_loop_mode_state_t test_loop_mode = TEST_LOOP_INACTIVE;
srsran::timer_handler::unique_timer pdu_delay_timer;
std::map<uint32_t, block_queue<srsran::unique_byte_buffer_t> > pdu_queue; // A PDU queue for each DRB
block_queue<srsran::unique_byte_buffer_t> pdu_queue; // PDU for UL data
tft_pdu_matcher tft_matcher;
int default_eps_bearer_id = -1;
all_args_t args = {};
};

View File

@ -72,6 +72,8 @@ all_args_t parse_args(ttcn3_dut_args_t* args, int argc, char* argv[])
all_args_t all_args = {};
all_args.stack.pkt_trace.enable = "mac,nas";
all_args.stack.pkt_trace.mac_pcap.enable = args->mac_pcap.enable;
all_args.stack.pkt_trace.mac_pcap.filename = args->mac_pcap.filename;
@ -83,6 +85,8 @@ all_args_t parse_args(ttcn3_dut_args_t* args, int argc, char* argv[])
all_args.log.all_hex_limit = args->log_hex_level;
all_args.phy.log.phy_level = args->log_level;
all_args.stack.log.stack_level = args->log_level;
all_args.stack.log.mac_level = args->log_level;
all_args.stack.log.rlc_level = args->log_level;
all_args.stack.log.pdcp_level = args->log_level;
@ -91,6 +95,7 @@ all_args_t parse_args(ttcn3_dut_args_t* args, int argc, char* argv[])
all_args.stack.log.gw_level = args->log_level;
all_args.stack.log.usim_level = args->log_level;
all_args.phy.log.phy_hex_limit = args->log_hex_level;
all_args.stack.log.stack_hex_limit = args->log_hex_level;
all_args.stack.log.mac_hex_limit = args->log_hex_level;
all_args.stack.log.rlc_hex_limit = args->log_hex_level;
all_args.stack.log.pdcp_hex_limit = args->log_hex_level;

View File

@ -153,6 +153,7 @@ void ttcn3_ue::add_mch_port(uint32_t lcid, uint32_t port) {}
void ttcn3_ue::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
{
logger.debug(pdu->msg, pdu->N_bytes, "Rx PDU (%d B) on lcid=%d", pdu->N_bytes, lcid);
switch (test_loop_mode) {
case TEST_LOOP_INACTIVE:
logger.warning("Test loop inactive. Dropping PDU.");
@ -163,13 +164,13 @@ void ttcn3_ue::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
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));
pdu_queue.push(std::move(pdu));
} else {
if (pdu_delay_timer.is_set()) {
pdu_queue[lcid].push(std::move(pdu));
pdu_queue.push(std::move(pdu));
pdu_delay_timer.run(); // timer is already set
} else {
loop_back_pdu_with_tft(lcid, std::move(pdu));
loop_back_pdu_with_tft(std::move(pdu));
}
}
break;
@ -180,12 +181,12 @@ void ttcn3_ue::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu)
}
void ttcn3_ue::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {}
int ttcn3_ue::setup_if_addr(uint32_t eps_bearer_id,
uint32_t lcid,
uint8_t pdn_type,
uint32_t ip_addr,
uint8_t* ipv6_if_id,
char* err_str)
{
default_eps_bearer_id = static_cast<int32_t>(eps_bearer_id);
return 0;
}
@ -194,16 +195,16 @@ bool ttcn3_ue::is_running()
return true;
}
int ttcn3_ue::update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid)
int ttcn3_ue::deactivate_eps_bearer(const uint32_t eps_bearer_id)
{
default_eps_bearer_id = -1;
return SRSRAN_SUCCESS;
}
int ttcn3_ue::apply_traffic_flow_template(const uint8_t& eps_bearer_id,
const uint8_t& lcid,
const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft)
{
return tft_matcher.apply_traffic_flow_template(eps_bearer_id, lcid, tft);
return tft_matcher.apply_traffic_flow_template(eps_bearer_id, tft);
}
void ttcn3_ue::set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms_)
@ -249,18 +250,27 @@ void ttcn3_ue::send_queued_data()
return;
}
for (auto& bearer_pdu_queue : pdu_queue) {
logger.info("Delivering %zd buffered PDUs for LCID=%d", bearer_pdu_queue.second.size(), bearer_pdu_queue.first);
while (not bearer_pdu_queue.second.empty()) {
srsran::unique_byte_buffer_t pdu;
bearer_pdu_queue.second.try_pop(&pdu);
loop_back_pdu_with_tft(bearer_pdu_queue.first, std::move(pdu));
}
logger.info("Delivering %zd buffered UL PDUs", pdu_queue.size());
while (not pdu_queue.empty()) {
srsran::unique_byte_buffer_t pdu;
pdu_queue.try_pop(&pdu);
loop_back_pdu_with_tft(std::move(pdu));
}
}
void ttcn3_ue::loop_back_pdu_with_tft(uint32_t input_lcid, srsran::unique_byte_buffer_t pdu)
void ttcn3_ue::loop_back_pdu_with_tft(srsran::unique_byte_buffer_t pdu)
{
logger.info(pdu->msg, pdu->N_bytes, "Rx PDU (%d B) on lcid=%d, looping back", pdu->N_bytes, input_lcid);
stack->write_sdu(input_lcid, std::move(pdu));
if (default_eps_bearer_id == -1) {
logger.error("No default EPS bearer activated. Dropping PDU.");
}
uint8_t target_eps_bearer_id = default_eps_bearer_id;
if (tft_matcher.check_tft_filter_match(pdu, target_eps_bearer_id) == SRSRAN_SUCCESS) {
logger.debug("Updated EPS bearer");
}
logger.info(
pdu->msg, pdu->N_bytes, "Rx PDU (%d B) looping back on eps_bearer_id=%d", pdu->N_bytes, target_eps_bearer_id);
stack->write_sdu(target_eps_bearer_id, std::move(pdu));
}