Fix GTPU RRC reestablishment handling

- GTPU bearers rnti is correctly updated during RRC reestablishment
- Create rnti_map_t typedef for circular maps of rntis
- Failed GTPU rnti creation is now handled
- Extend indirect GTPU tunnel timeout to 2000msec
- Support EIA0 during S1 Handover
This commit is contained in:
Francisco 2021-05-10 15:22:02 +01:00
parent 24a7ea9c6a
commit cdd3932e73
10 changed files with 74 additions and 22 deletions

View File

@ -37,9 +37,20 @@ class static_circular_map
using obj_t = std::pair<K, T>;
public:
using key_type = K;
using mapped_type = T;
using value_type = std::pair<K, T>;
using difference_type = std::ptrdiff_t;
class iterator
{
public:
using iterator_category = std::forward_iterator_tag;
using value_type = std::pair<K, T>;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
iterator() = default;
iterator(static_circular_map<K, T, N>* map, size_t idx_) : ptr(map), idx(idx_)
{

View File

@ -26,6 +26,7 @@
INCLUDES
*******************************************************************************/
#include "srsran/adt/circular_map.h"
#include "srsran/common/common_lte.h"
#include <stdint.h>
@ -57,6 +58,10 @@ constexpr uint32_t drb_to_lcid(lte_drb drb_id)
#define SRSENB_MAX_BUFFER_SIZE_BYTES 12756
#define SRSENB_BUFFER_HEADER_OFFSET 1024
/// Typedef of circular map container which key corresponding to rnti value and that can be used across layers
template <typename UEObject>
using rnti_map_t = srsran::static_circular_map<uint16_t, UEObject, SRSENB_MAX_UES>;
} // namespace srsenb
#endif // SRSENB_COMMON_ENB_H

View File

@ -145,9 +145,9 @@ private:
sched_interface::dl_pdu_mch_t mch = {};
/* Map of active UEs */
srsran::static_circular_map<uint16_t, std::unique_ptr<ue>, 64> ue_db;
std::map<uint16_t, std::unique_ptr<ue> > ues_to_rem;
uint16_t last_rnti = 70;
rnti_map_t<std::unique_ptr<ue> > ue_db;
std::map<uint16_t, std::unique_ptr<ue> > ues_to_rem;
uint16_t last_rnti = 70;
srsran::static_blocking_queue<std::unique_ptr<ue>, 32> ue_pool; ///< Pool of pre-allocated UE objects
void prealloc_ue(uint32_t nof_ue);

View File

@ -88,6 +88,9 @@ public:
bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_, gtpu_interface_rrc* gtpu_);
/// Called after RRCReestablishmentComplete, to add E-RABs of old rnti
void reestablish_bearers(bearer_cfg_handler&& old_rnti_bearers);
int add_erab(uint8_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos,
const asn1::bounded_bitstring<1, 160, true, true>& addr,

View File

@ -128,8 +128,8 @@ private:
pdcp_interface_gtpu* pdcp = nullptr;
srslog::basic_logger& logger;
srsran::static_circular_map<uint16_t, ue_lcid_tunnel_list, SRSENB_MAX_UES> ue_teidin_db;
tunnel_list_t tunnels;
rnti_map_t<ue_lcid_tunnel_list> ue_teidin_db;
tunnel_list_t tunnels;
};
using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state;

View File

@ -114,6 +114,8 @@ bool security_cfg_handler::set_security_capabilities(const asn1::s1ap::ue_securi
case srsran::INTEGRITY_ALGORITHM_ID_EIA0:
// Null integrity is not supported
logger.info("Skipping EIA0 as RRC integrity algorithm. Null integrity is not supported.");
sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_EIA0;
integ_algo_found = true;
break;
case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1:
// “first bit” 128-EIA1,
@ -211,6 +213,14 @@ bearer_cfg_handler::bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_, gt
rnti(rnti_), cfg(&cfg_), gtpu(gtpu_), logger(&srslog::fetch_basic_logger("RRC"))
{}
void bearer_cfg_handler::reestablish_bearers(bearer_cfg_handler&& old_rnti_bearers)
{
erab_info_list = std::move(old_rnti_bearers.erab_info_list);
erabs = std::move(old_rnti_bearers.erabs);
current_drbs = std::move(old_rnti_bearers.current_drbs);
old_rnti_bearers.current_drbs.clear();
}
int bearer_cfg_handler::add_erab(uint8_t erab_id,
const asn1::s1ap::erab_level_qos_params_s& qos,
const asn1::bounded_bitstring<1, 160, true, true>& addr,

View File

@ -96,6 +96,8 @@ uint16_t compute_mac_i(uint16_t crnti,
// Compute MAC-I
switch (integ_algo) {
case srsran::INTEGRITY_ALGORITHM_ID_EIA0:
return 0;
case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1:
srsran::security_128_eia1(&k_rrc_int[16],
0xffffffff, // 32-bit all to ones
@ -115,7 +117,7 @@ uint16_t compute_mac_i(uint16_t crnti,
mac_key);
break;
default:
printf("Unsupported integrity algorithm %d.", integ_algo);
srsran::console_stderr("ERROR: Unsupported integrity algorithm %d.\n", integ_algo);
}
uint16_t short_mac_i = (((uint16_t)mac_key[2] << 8u) | (uint16_t)mac_key[3]);

View File

@ -523,10 +523,16 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
static_cast<unsigned>(rrc_event_type::con_reest_req),
static_cast<unsigned>(procedure_result_code::none),
rnti);
const rrc_conn_reest_request_r8_ies_s& req_r8 = msg->crit_exts.rrc_conn_reest_request_r8();
uint16_t old_rnti = req_r8.ue_id.c_rnti.to_number();
srsran::console(
"User 0x%x requesting RRC Reestablishment as 0x%x. Cause: %s\n", rnti, old_rnti, req_r8.reest_cause.to_string());
if (not parent->s1ap->is_mme_connected()) {
parent->logger.error("MME isn't connected. Sending Connection Reject");
send_connection_reject(procedure_result_code::error_mme_not_connected);
send_connection_reest_rej(procedure_result_code::error_mme_not_connected);
srsran::console("User 0x%x RRC Reestablishment Request rejected\n", rnti);
return;
}
parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s",
@ -535,7 +541,6 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
(uint32_t)msg->crit_exts.rrc_conn_reest_request_r8().ue_id.short_mac_i.to_number(),
msg->crit_exts.rrc_conn_reest_request_r8().reest_cause.to_string());
if (is_idle()) {
uint16_t old_rnti = msg->crit_exts.rrc_conn_reest_request_r8().ue_id.c_rnti.to_number();
uint16_t old_pci = msg->crit_exts.rrc_conn_reest_request_r8().ue_id.pci;
const enb_cell_common* old_cell = parent->cell_common_list->get_pci(old_pci);
auto ue_it = parent->users.find(old_rnti);
@ -590,9 +595,13 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg)
} else {
parent->logger.error("Received ConnectionReestablishment for rnti=0x%x without context", old_rnti);
send_connection_reest_rej(procedure_result_code::error_unknown_rnti);
srsran::console(
"User 0x%x RRC Reestablishment Request rejected. Cause: no rnti=0x%x context available\n", rnti, old_rnti);
}
} else {
parent->logger.error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE", rnti);
send_connection_reest_rej(procedure_result_code::error_unknown_rnti);
srsran::console("ERROR: User 0x%x requesting Reestablishment is not in RRC_IDLE\n", rnti);
}
}
@ -663,8 +672,8 @@ void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsr
parent->pdcp->enable_integrity(rnti, srb_to_lcid(lte_srb::srb1));
parent->pdcp->enable_encryption(rnti, srb_to_lcid(lte_srb::srb1));
// Reestablish current DRBs during ConnectionReconfiguration
bearer_list = std::move(parent->users.at(old_reest_rnti)->bearer_list);
// Reestablish E-RABs of old rnti during ConnectionReconfiguration
bearer_list.reestablish_bearers(std::move(parent->users.at(old_reest_rnti)->bearer_list));
// remove old RNTI
parent->rem_user_thread(old_reest_rnti);

View File

@ -99,7 +99,11 @@ const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid,
tun->spgw_addr = spgw_addr;
if (not ue_teidin_db.contains(rnti)) {
ue_teidin_db.insert(rnti, ue_lcid_tunnel_list());
auto ret = ue_teidin_db.insert(rnti, ue_lcid_tunnel_list());
if (ret.is_error()) {
logger.error("Failed to allocate rnti=0x%x", rnti);
return nullptr;
}
}
auto& ue_tunnels = ue_teidin_db[rnti];
@ -130,16 +134,20 @@ bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti)
auto* old_rnti_ptr = find_rnti_tunnels(old_rnti);
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 (lcid_tunnel& bearer : *new_rnti_ptr) {
// create new RNTI and update TEIDs of old rnti to reflect new rnti
if (not ue_teidin_db.insert(new_rnti, ue_lcid_tunnel_list())) {
logger.error("Failure to create new rnti=0x%x", new_rnti);
return false;
}
std::swap(ue_teidin_db[new_rnti], *old_rnti_ptr);
auto& new_rnti_obj = ue_teidin_db[new_rnti];
for (lcid_tunnel& bearer : new_rnti_obj) {
tunnels[bearer.teid].rnti = new_rnti;
}
// Leave old_rnti as zombie to be removed later
old_rnti_ptr->clear();
return true;
}
@ -246,10 +254,14 @@ void gtpu_tunnel_manager::set_tunnel_priority(uint32_t before_teid, uint32_t aft
}
};
// Schedule auto-removal of this indirect tunnel
// Schedule auto-removal of the indirect tunnel in case the End Marker is not received
// TS 36.300 - On detection of the "end marker", the target eNB may also initiate the release of the data forwarding
// resource. However, the release of the data forwarding resource is implementation dependent and could
// also be based on other mechanisms (e.g. timer-based mechanism).
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
before_tun.rx_timer.set(2000, [this, before_teid](uint32_t tid) {
// Note: This will self-destruct the callback object
logger.info("Forwarding tunnel " TEID_IN_FMT "being closed after timeout=2000 msec", before_teid);
remove_tunnel(before_teid);
});
before_tun.rx_timer.run();

View File

@ -329,7 +329,7 @@ int test_gtpu_direct_tunneling(tunnel_test_event event)
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) {
for (size_t i = 0; i < 2001; ++i) {
task_sched.tic();
}
} else {