Introducing extended NGAP functionality

Remove unused logger for now in ngap::ue to pass tests
This commit is contained in:
Bedran Karakoc 2021-06-15 15:09:34 +02:00 committed by Bedran Karakoc
parent 5a82a63bb5
commit c280ac5ad6
6 changed files with 711 additions and 41 deletions

View File

@ -42,17 +42,14 @@ public:
uint32_t gnb_cc_idx,
asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu,
uint32_t m_tmsi,
uint8_t mmec) = 0;
uint32_t m_tmsi) = 0;
virtual void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu) = 0;
virtual bool user_exists(uint16_t rnti) = 0;
virtual void user_mod(uint16_t old_rnti, uint16_t new_rnti) = 0;
virtual bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) = 0;
virtual bool is_amf_connected() = 0;
/// TS 36.413, 8.3.1 - Initial Context Setup
virtual void ue_ctxt_setup_complete(uint16_t rnti) = 0;
virtual void ue_ctxt_setup_complete(uint16_t rnti) = 0;
};
} // namespace srsenb

View File

@ -14,6 +14,7 @@
#include "srsenb/hdr/common/common_enb.h"
#include "srsran/adt/circular_map.h"
#include "srsran/adt/optional.h"
#include "srsran/asn1/asn1_utils.h"
#include "srsran/asn1/ngap.h"
#include "srsran/common/bcd_helpers.h"
@ -27,8 +28,11 @@
#include "srsran/interfaces/gnb_ngap_interfaces.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include "srsran/srslog/srslog.h"
#include <iostream>
#include <unordered_map>
namespace srsenb {
class ngap : public ngap_interface_rrc_nr
{
public:
@ -42,21 +46,22 @@ public:
void initial_ue(uint16_t rnti,
uint32_t gnb_cc_idx,
asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu){};
srsran::unique_byte_buffer_t pdu);
void initial_ue(uint16_t rnti,
uint32_t gnb_cc_idx,
asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu,
uint32_t m_tmsi,
uint8_t mmec){};
uint32_t s_tmsi);
void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu){};
bool user_exists(uint16_t rnti) { return true; };
void user_mod(uint16_t old_rnti, uint16_t new_rnti){};
bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) { return true; };
bool is_amf_connected();
/// TS 36.413, 8.3.1 - Initial Context Setup
void ue_ctxt_setup_complete(uint16_t rnti){};
bool send_error_indication(const asn1::ngap_nr::cause_c& cause,
srsran::optional<uint32_t> ran_ue_ngap_id = {},
srsran::optional<uint32_t> amf_ue_ngap_id = {});
void ue_ctxt_setup_complete(uint16_t rnti);
// Stack interface
bool
@ -82,7 +87,7 @@ private:
struct sockaddr_in amf_addr = {}; // AMF address
bool amf_connected = false;
bool running = false;
uint32_t next_enb_ue_ngap_id = 1; // Next ENB-side UE identifier
uint32_t next_gnb_ue_ngap_id = 1; // Next GNB-side UE identifier
uint16_t next_ue_stream_id = 1; // Next UE SCTP stream identifier
srsran::unique_timer amf_connect_timer, ngsetup_timeout;
@ -90,8 +95,82 @@ private:
asn1::ngap_nr::tai_s tai;
asn1::ngap_nr::nr_cgi_s nr_cgi;
// Moved into NGAP class to avoid redifinition (Introduce new namespace?)
struct ue_ctxt_t {
static const uint32_t invalid_gnb_id = std::numeric_limits<uint32_t>::max();
uint16_t rnti = SRSRAN_INVALID_RNTI;
uint32_t ran_ue_ngap_id = invalid_gnb_id;
srsran::optional<uint32_t> amf_ue_ngap_id;
uint32_t gnb_cc_idx = 0;
struct timeval init_timestamp = {};
// AMF identifier
uint16_t amf_set_id;
uint8_t amf_pointer;
uint8_t amf_region_id;
};
asn1::ngap_nr::ng_setup_resp_s ngsetupresponse;
int build_tai_cgi();
bool connect_amf();
bool setup_ng();
bool sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name);
bool handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu);
bool handle_successfuloutcome(const asn1::ngap_nr::successful_outcome_s& msg);
bool handle_unsuccessfuloutcome(const asn1::ngap_nr::unsuccessful_outcome_s& msg);
bool handle_initiatingmessage(const asn1::ngap_nr::init_msg_s& msg);
bool handle_dlnastransport(const asn1::ngap_nr::dl_nas_transport_s& msg);
bool handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg);
bool handle_ngsetupfailure(const asn1::ngap_nr::ng_setup_fail_s& msg);
bool handle_initialctxtsetuprequest(const asn1::ngap_nr::init_context_setup_request_s& msg);
struct ue {
explicit ue(ngap* ngap_ptr_);
bool send_initialuemessage(asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu,
bool has_tmsi,
uint32_t s_tmsi = 0);
bool send_ulnastransport(srsran::unique_byte_buffer_t pdu);
bool was_uectxtrelease_requested() const { return release_requested; }
void ue_ctxt_setup_complete();
ue_ctxt_t ctxt = {};
uint16_t stream_id = 1;
private:
// args
ngap* ngap_ptr;
// state
bool release_requested = false;
};
class user_list
{
public:
using value_type = std::unique_ptr<ue>;
using iterator = std::unordered_map<uint32_t, value_type>::iterator;
using const_iterator = std::unordered_map<uint32_t, value_type>::const_iterator;
using pair_type = std::unordered_map<uint32_t, value_type>::value_type;
ue* find_ue_rnti(uint16_t rnti);
ue* find_ue_gnbid(uint32_t gnbid);
ue* find_ue_amfid(uint32_t amfid);
ue* add_user(value_type user);
void erase(ue* ue_ptr);
iterator begin() { return users.begin(); }
iterator end() { return users.end(); }
const_iterator cbegin() const { return users.begin(); }
const_iterator cend() const { return users.end(); }
size_t size() const { return users.size(); }
private:
std::unordered_map<uint32_t, std::unique_ptr<ue> > users; // maps ran_ue_ngap_id to user
};
user_list users;
// procedures
class ng_setup_proc_t
{
@ -114,15 +193,7 @@ private:
ngap* ngap_ptr = nullptr;
};
void build_tai_cgi();
bool connect_amf();
bool setup_ng();
bool sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name);
bool handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu);
bool handle_successfuloutcome(const asn1::ngap_nr::successful_outcome_s& msg);
bool handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg);
ue* handle_ngapmsg_ue_id(uint32_t gnb_id, uint32_t amf_id);
srsran::proc_t<ng_setup_proc_t> ngsetup_proc;

View File

@ -11,6 +11,10 @@
*/
#include "srsenb/hdr/stack/ngap/ngap.h"
#include "srsran/common/int_helpers.h"
using srsran::s1ap_mccmnc_to_plmn;
using srsran::uint32_to_uint8;
#define procError(fmt, ...) ngap_ptr->logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define procWarning(fmt, ...) ngap_ptr->logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
@ -145,20 +149,137 @@ bool ngap::is_amf_connected()
}
// Generate common NGAP protocol IEs from config args
void ngap::build_tai_cgi()
int ngap::build_tai_cgi()
{
uint32_t plmn;
uint8_t shift;
// TAI
srsran::s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn);
tai.plmn_id.from_number(plmn);
tai.tac.from_number(args.tac);
// nr_cgi
// NR CGI
nr_cgi.plmn_id.from_number(plmn);
// TODO Check how to build nr cell id
nr_cgi.nrcell_id.from_number((uint32_t)(args.gnb_id << 8) | args.cell_id);
// NR CELL ID (36 bits) = gnb_id (22...32 bits) + cell_id (4...14 bits)
if (((uint8_t)log2(args.gnb_id) + (uint8_t)log2(args.cell_id) + 2) > 36) {
logger.error("gNB ID and Cell ID combination greater than 36 bits");
return SRSRAN_ERROR;
}
// Consider moving sanity checks into the parsing function of the configs.
if (((uint8_t)log2(args.gnb_id) + 1) < 22) {
shift = 14;
} else {
shift = 36 - ((uint8_t)log2(args.gnb_id) + 1);
}
nr_cgi.nrcell_id.from_number((uint64_t)args.gnb_id << shift | args.cell_id);
return SRSRAN_SUCCESS;
}
/*******************************************************************************
/* RRC interface
********************************************************************************/
void ngap::initial_ue(uint16_t rnti,
uint32_t gnb_cc_idx,
asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu)
{
std::unique_ptr<ue> ue_ptr{new ue{this}};
ue_ptr->ctxt.rnti = rnti;
ue_ptr->ctxt.gnb_cc_idx = gnb_cc_idx;
ue* u = users.add_user(std::move(ue_ptr));
if (u == nullptr) {
logger.error("Failed to add rnti=0x%x", rnti);
return;
}
u->send_initialuemessage(cause, std::move(pdu), false);
}
void ngap::initial_ue(uint16_t rnti,
uint32_t gnb_cc_idx,
asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu,
uint32_t s_tmsi)
{
std::unique_ptr<ue> ue_ptr{new ue{this}};
ue_ptr->ctxt.rnti = rnti;
ue_ptr->ctxt.gnb_cc_idx = gnb_cc_idx;
ue* u = users.add_user(std::move(ue_ptr));
if (u == nullptr) {
logger.error("Failed to add rnti=0x%x", rnti);
return;
}
u->send_initialuemessage(cause, std::move(pdu), true, s_tmsi);
}
void ngap::ue_ctxt_setup_complete(uint16_t rnti)
{
ue* u = users.find_ue_rnti(rnti);
if (u == nullptr) {
return;
}
u->ue_ctxt_setup_complete();
}
/*********************************************************
* ngap::user_list class
*********************************************************/
ngap::ue* ngap::user_list::find_ue_rnti(uint16_t rnti)
{
if (rnti == SRSRAN_INVALID_RNTI) {
return nullptr;
}
auto it = std::find_if(
users.begin(), users.end(), [rnti](const user_list::pair_type& v) { return v.second->ctxt.rnti == rnti; });
return it != users.end() ? it->second.get() : nullptr;
}
ngap::ue* ngap::user_list::find_ue_gnbid(uint32_t gnbid)
{
auto it = users.find(gnbid);
return (it != users.end()) ? it->second.get() : nullptr;
}
ngap::ue* ngap::user_list::find_ue_amfid(uint32_t amfid)
{
auto it = std::find_if(users.begin(), users.end(), [amfid](const user_list::pair_type& v) {
return v.second->ctxt.amf_ue_ngap_id == amfid;
});
return it != users.end() ? it->second.get() : nullptr;
}
ngap::ue* ngap::user_list::add_user(std::unique_ptr<ngap::ue> user)
{
static srslog::basic_logger& logger = srslog::fetch_basic_logger("NGAP");
// Check for ID repetitions
if (find_ue_rnti(user->ctxt.rnti) != nullptr) {
logger.error("The user to be added with rnti=0x%x already exists", user->ctxt.rnti);
return nullptr;
}
if (find_ue_gnbid(user->ctxt.ran_ue_ngap_id) != nullptr) {
logger.error("The user to be added with ran ue ngap id=%d already exists", user->ctxt.ran_ue_ngap_id);
return nullptr;
}
if (user->ctxt.amf_ue_ngap_id.has_value() and find_ue_amfid(user->ctxt.amf_ue_ngap_id.value()) != nullptr) {
logger.error("The user to be added with amf id=%d already exists", user->ctxt.amf_ue_ngap_id.value());
return nullptr;
}
auto p = users.insert(std::make_pair(user->ctxt.ran_ue_ngap_id, std::move(user)));
return p.second ? p.first->second.get() : nullptr;
}
void ngap::user_list::erase(ue* ue_ptr)
{
static srslog::basic_logger& logger = srslog::fetch_basic_logger("NGAP");
auto it = users.find(ue_ptr->ctxt.ran_ue_ngap_id);
if (it == users.end()) {
logger.error("User to be erased does not exist");
return;
}
users.erase(it);
}
/*******************************************************************************
@ -191,11 +312,11 @@ bool ngap::handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu,
amf_socket.close();
}
// Restart MME connection procedure if we lost connection
// Restart AMF connection procedure if we lost connection
if (not amf_socket.is_open()) {
amf_connected = false;
if (ngsetup_proc.is_busy()) {
logger.error("Failed to initiate MME connection procedure, as it is already running.");
logger.error("Failed to initiate AMF connection procedure, as it is already running.");
return false;
}
ngsetup_proc.launch();
@ -208,6 +329,7 @@ bool ngap::handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu,
bool ngap::handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu)
{
// TODO:
// Save message to PCAP
// if (pcap != nullptr) {
// pcap->write_ngap(pdu->msg, pdu->N_bytes);
@ -220,18 +342,19 @@ bool ngap::handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu)
logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack received PDU");
cause_c cause;
cause.set_protocol().value = cause_protocol_opts::transfer_syntax_error;
// send_error_indication(cause);
send_error_indication(cause);
return false;
}
// TODO:
// log_ngap_msg(rx_pdu, srsran::make_span(*pdu), true);
switch (rx_pdu.type().value) {
// case ngap_pdu_c::types_opts::init_msg:
// return handle_initiatingmessage(rx_pdu.init_msg());
case ngap_pdu_c::types_opts::init_msg:
return handle_initiatingmessage(rx_pdu.init_msg());
case ngap_pdu_c::types_opts::successful_outcome:
return handle_successfuloutcome(rx_pdu.successful_outcome());
// case ngap_pdu_c::types_opts::unsuccessful_outcome:
// return handle_unsuccessfuloutcome(rx_pdu.unsuccessful_outcome());
case ngap_pdu_c::types_opts::unsuccessful_outcome:
return handle_unsuccessfuloutcome(rx_pdu.unsuccessful_outcome());
default:
logger.error("Unhandled PDU type %d", rx_pdu.type().value);
return false;
@ -240,6 +363,19 @@ bool ngap::handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu)
return true;
}
bool ngap::handle_initiatingmessage(const asn1::ngap_nr::init_msg_s& msg)
{
switch (msg.value.type().value) {
case ngap_elem_procs_o::init_msg_c::types_opts::dl_nas_transport:
return handle_dlnastransport(msg.value.dl_nas_transport());
case ngap_elem_procs_o::init_msg_c::types_opts::init_context_setup_request:
return handle_initialctxtsetuprequest(msg.value.init_context_setup_request());
default:
logger.error("Unhandled initiating message: %s", msg.value.type().to_string());
}
return true;
}
bool ngap::handle_successfuloutcome(const successful_outcome_s& msg)
{
switch (msg.value.type().value) {
@ -251,6 +387,17 @@ bool ngap::handle_successfuloutcome(const successful_outcome_s& msg)
return true;
}
bool ngap::handle_unsuccessfuloutcome(const asn1::ngap_nr::unsuccessful_outcome_s& msg)
{
switch (msg.value.type().value) {
case ngap_elem_procs_o::unsuccessful_outcome_c::types_opts::ng_setup_fail:
return handle_ngsetupfailure(msg.value.ng_setup_fail());
default:
logger.error("Unhandled unsuccessful outcome message: %s", msg.value.type().to_string());
}
return true;
}
bool ngap::handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg)
{
ngsetupresponse = msg;
@ -258,6 +405,93 @@ bool ngap::handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg)
ng_setup_proc_t::ngsetupresult res;
res.success = true;
ngsetup_proc.trigger(res);
return true;
}
bool ngap::handle_ngsetupfailure(const asn1::ngap_nr::ng_setup_fail_s& msg)
{
std::string cause = get_cause(msg.protocol_ies.cause.value);
logger.error("NG Setup Failure. Cause: %s", cause.c_str());
srsran::console("NG Setup Failure. Cause: %s\n", cause.c_str());
return true;
}
bool ngap::handle_dlnastransport(const asn1::ngap_nr::dl_nas_transport_s& msg)
{
if (msg.ext) {
logger.warning("Not handling NGAP message extension");
}
ue* u =
handle_ngapmsg_ue_id(msg.protocol_ies.ran_ue_ngap_id.value.value, msg.protocol_ies.amf_ue_ngap_id.value.value);
if (u == nullptr) {
return false;
}
if (msg.protocol_ies.old_amf_present) {
logger.warning("Not handling OldAMF");
}
if (msg.protocol_ies.ran_paging_prio_present) {
logger.warning("Not handling RANPagingPriority");
}
if (msg.protocol_ies.mob_restrict_list_present) {
logger.warning("Not handling MobilityRestrictionList");
}
if (msg.protocol_ies.idx_to_rfsp_present) {
logger.warning("Not handling IndexToRFSP");
}
if (msg.protocol_ies.ue_aggregate_maximum_bit_rate_present) {
logger.warning("Not handling UEAggregateMaximumBitRate");
}
if (msg.protocol_ies.allowed_nssai_present) {
logger.warning("Not handling AllowedNSSAI");
}
// TODO: Pass NAS PDU once RRC interface is ready
/* srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer();
if (pdu == nullptr) {
logger.error("Fatal Error: Couldn't allocate buffer in ngap::run_thread().");
return false;
}
memcpy(pdu->msg, msg.protocol_ies.nas_pdu.value.data(), msg.protocol_ies.nas_pdu.value.size());
pdu->N_bytes = msg.protocol_ies.nas_pdu.value.size();
rrc->write_dl_info(u->ctxt.rnti, std::move(pdu)); */
return true;
}
bool ngap::handle_initialctxtsetuprequest(const asn1::ngap_nr::init_context_setup_request_s& msg)
{
ue* u =
handle_ngapmsg_ue_id(msg.protocol_ies.ran_ue_ngap_id.value.value, msg.protocol_ies.amf_ue_ngap_id.value.value);
if (u == nullptr) {
return false;
}
u->ctxt.amf_pointer = msg.protocol_ies.guami.value.amf_pointer.to_number();
u->ctxt.amf_set_id = msg.protocol_ies.guami.value.amf_set_id.to_number();
u->ctxt.amf_region_id = msg.protocol_ies.guami.value.amf_region_id.to_number();
// Setup UE ctxt in RRC once interface is ready
/* if (not rrc->setup_ue_ctxt(u->ctxt.rnti, msg)) {
return false;
} */
/* if (msg.protocol_ies.nas_pdu_present) {
srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer();
if (pdu == nullptr) {
logger.error("Fatal Error: Couldn't allocate buffer in ngap::run_thread().");
return false;
}
memcpy(pdu->msg, msg.protocol_ies.nas_pdu.value.data(), msg.protocol_ies.nas_pdu.value.size());
pdu->N_bytes = msg.protocol_ies.nas_pdu.value.size();
rrc->write_dl_info(u->ctxt.rnti, std::move(pdu));
} */
return true;
}
@ -344,6 +578,134 @@ bool ngap::setup_ng()
return sctp_send_ngap_pdu(pdu, 0, "ngSetupRequest");
}
/*******************************************************************************
/* NGAP message senders
********************************************************************************/
bool ngap::ue::send_initialuemessage(asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu,
bool has_tmsi,
uint32_t s_tmsi)
{
if (not ngap_ptr->amf_connected) {
return false;
}
ngap_pdu_c tx_pdu;
tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_INIT_UE_MSG);
init_ue_msg_ies_container& container = tx_pdu.init_msg().value.init_ue_msg().protocol_ies;
// 5G-S-TMSI
if (has_tmsi) {
container.five_g_s_tmsi_present = true;
srsran::uint32_to_uint8(s_tmsi, container.five_g_s_tmsi.value.five_g_tmsi.data());
container.five_g_s_tmsi.value.amf_set_id.from_number(ctxt.amf_set_id);
container.five_g_s_tmsi.value.amf_pointer.from_number(ctxt.amf_pointer);
}
// RAN_UE_NGAP_ID
container.ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id;
// NAS_PDU
container.nas_pdu.value.resize(pdu->N_bytes);
memcpy(container.nas_pdu.value.data(), pdu->msg, pdu->N_bytes);
// RRC Establishment Cause
container.rrcestablishment_cause.value = cause;
// User Location Info
// userLocationInformationNR
container.user_location_info.value.set_user_location_info_nr();
container.user_location_info.value.user_location_info_nr().nr_cgi.nrcell_id = ngap_ptr->nr_cgi.nrcell_id;
container.user_location_info.value.user_location_info_nr().nr_cgi.plmn_id = ngap_ptr->nr_cgi.plmn_id;
container.user_location_info.value.user_location_info_nr().tai.plmn_id = ngap_ptr->tai.plmn_id;
container.user_location_info.value.user_location_info_nr().tai.tac = ngap_ptr->tai.tac;
return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "InitialUEMessage");
}
bool ngap::ue::send_ulnastransport(srsran::unique_byte_buffer_t pdu)
{
if (not ngap_ptr->amf_connected) {
return false;
}
ngap_pdu_c tx_pdu;
tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_UL_NAS_TRANSPORT);
asn1::ngap_nr::ul_nas_transport_ies_container& container = tx_pdu.init_msg().value.ul_nas_transport().protocol_ies;
// AMF UE NGAP ID
container.amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value();
// RAN UE NGAP ID
container.ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id;
// NAS PDU
container.nas_pdu.value.resize(pdu->N_bytes);
memcpy(container.nas_pdu.value.data(), pdu->msg, pdu->N_bytes);
// User Location Info
// userLocationInformationNR
container.user_location_info.value.set_user_location_info_nr();
container.user_location_info.value.user_location_info_nr().nr_cgi.nrcell_id = ngap_ptr->nr_cgi.nrcell_id;
container.user_location_info.value.user_location_info_nr().nr_cgi.plmn_id = ngap_ptr->nr_cgi.plmn_id;
container.user_location_info.value.user_location_info_nr().tai.plmn_id = ngap_ptr->tai.plmn_id;
container.user_location_info.value.user_location_info_nr().tai.tac = ngap_ptr->tai.tac;
return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "UplinkNASTransport");
}
void ngap::ue::ue_ctxt_setup_complete()
{
ngap_pdu_c tx_pdu;
// Handle PDU Session List once RRC interface is ready
tx_pdu.set_successful_outcome().load_info_obj(ASN1_NGAP_NR_ID_INIT_CONTEXT_SETUP);
auto& container = tx_pdu.successful_outcome().value.init_context_setup_resp().protocol_ies;
}
bool ngap::send_error_indication(const asn1::ngap_nr::cause_c& cause,
srsran::optional<uint32_t> ran_ue_ngap_id,
srsran::optional<uint32_t> amf_ue_ngap_id)
{
if (not amf_connected) {
return false;
}
ngap_pdu_c tx_pdu;
tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_ERROR_IND);
auto& container = tx_pdu.init_msg().value.error_ind().protocol_ies;
uint16_t rnti = SRSRAN_INVALID_RNTI;
container.ran_ue_ngap_id_present = ran_ue_ngap_id.has_value();
if (ran_ue_ngap_id.has_value()) {
container.ran_ue_ngap_id.value = ran_ue_ngap_id.value();
ue* user_ptr = users.find_ue_gnbid(ran_ue_ngap_id.value());
rnti = user_ptr != nullptr ? user_ptr->ctxt.rnti : SRSRAN_INVALID_RNTI;
}
container.amf_ue_ngap_id_present = amf_ue_ngap_id.has_value();
if (amf_ue_ngap_id.has_value()) {
container.amf_ue_ngap_id.value = amf_ue_ngap_id.value();
}
container.cause_present = true;
container.cause.value = cause;
return sctp_send_ngap_pdu(tx_pdu, rnti, "Error Indication");
}
/*******************************************************************************
/* ngap::ue Class
********************************************************************************/
ngap::ue::ue(ngap* ngap_ptr_) : ngap_ptr(ngap_ptr_)
{
ctxt.ran_ue_ngap_id = ngap_ptr->next_gnb_ue_ngap_id++;
gettimeofday(&ctxt.init_timestamp, nullptr);
stream_id = ngap_ptr->next_ue_stream_id;
}
/*******************************************************************************
/* General helpers
********************************************************************************/
@ -374,14 +736,14 @@ bool ngap::sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t
// }
if (rnti != SRSRAN_INVALID_RNTI) {
logger.info(buf->msg, buf->N_bytes, "Tx S1AP SDU, %s, rnti=0x%x", procedure_name, rnti);
logger.info(buf->msg, buf->N_bytes, "Tx NGAP SDU, %s, rnti=0x%x", procedure_name, rnti);
} else {
logger.info(buf->msg, buf->N_bytes, "Tx S1AP SDU, %s", procedure_name);
logger.info(buf->msg, buf->N_bytes, "Tx NGAP SDU, %s", procedure_name);
}
// TODO: when user list is ready
// uint16_t streamid = rnti == SRSRAN_INVALID_RNTI ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id;
uint16_t streamid = 0;
ssize_t n_sent = sctp_sendmsg(amf_socket.fd(),
uint16_t streamid = rnti == SRSRAN_INVALID_RNTI ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id;
ssize_t n_sent = sctp_sendmsg(amf_socket.fd(),
buf->msg,
buf->N_bytes,
(struct sockaddr*)&amf_addr,
@ -393,13 +755,87 @@ bool ngap::sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t
0);
if (n_sent == -1) {
if (rnti != SRSRAN_INVALID_RNTI) {
logger.error("Error: Failure at Tx S1AP SDU, %s, rnti=0x%x", procedure_name, rnti);
logger.error("Error: Failure at Tx NGAP SDU, %s, rnti=0x%x", procedure_name, rnti);
} else {
logger.error("Error: Failure at Tx S1AP SDU, %s", procedure_name);
logger.error("Error: Failure at Tx NGAP SDU, %s", procedure_name);
}
return false;
}
return true;
}
/**
* Helper method to find user based on the ran_ue_ngap_id stored in an S1AP Msg, and update amf_ue_ngap_id
* @param gnb_id ran_ue_ngap_id value stored in NGAP message
* @param amf_id amf_ue_ngap_id value stored in NGAP message
* @return pointer to user if it has been found
*/
ngap::ue* ngap::handle_ngapmsg_ue_id(uint32_t gnb_id, uint32_t amf_id)
{
ue* user_ptr = users.find_ue_gnbid(gnb_id);
ue* user_amf_ptr = nullptr;
cause_c cause;
// TODO: Introduce proper error handling for faulty ids
if (user_ptr != nullptr) {
if (user_ptr->ctxt.amf_ue_ngap_id == amf_id) {
return user_ptr;
}
user_amf_ptr = users.find_ue_amfid(amf_id);
if (not user_ptr->ctxt.amf_ue_ngap_id.has_value() and user_amf_ptr == nullptr) {
user_ptr->ctxt.amf_ue_ngap_id = amf_id;
return user_ptr;
}
logger.warning("AMF UE NGAP ID=%d not found - discarding message", amf_id);
if (user_amf_ptr != nullptr) {
cause.set_radio_network().value = cause_radio_network_opts::unknown_target_id;
}
} else {
user_amf_ptr = users.find_ue_amfid(amf_id);
logger.warning("RAN UE NGAP ID=%d not found - discarding message", gnb_id);
if (user_amf_ptr != nullptr) {
cause.set_radio_network().value = cause_radio_network_opts::unknown_local_ue_ngap_id;
}
}
send_error_indication(cause, gnb_id, amf_id);
if (user_ptr != nullptr) {
// rrc->release_ue(user_ptr->ctxt.rnti);
}
if (user_amf_ptr != nullptr and user_amf_ptr != user_ptr) {
// rrc->release_ue(user_mme_ptr->ctxt.rnti);
}
return nullptr;
}
std::string ngap::get_cause(const cause_c& c)
{
std::string cause = c.type().to_string();
cause += " - ";
switch (c.type().value) {
case cause_c::types_opts::radio_network:
cause += c.radio_network().to_string();
break;
case cause_c::types_opts::transport:
cause += c.transport().to_string();
break;
case cause_c::types_opts::nas:
cause += c.nas().to_string();
break;
case cause_c::types_opts::protocol:
cause += c.protocol().to_string();
break;
case cause_c::types_opts::misc:
cause += c.misc().to_string();
break;
default:
cause += "unknown";
break;
}
return cause;
}
} // namespace srsenb

View File

@ -11,6 +11,7 @@ add_subdirectory(phy)
add_subdirectory(upper)
add_subdirectory(rrc)
add_subdirectory(s1ap)
add_subdirectory(ngap)
add_executable(enb_metrics_test enb_metrics_test.cc ../src/metrics_stdout.cc ../src/metrics_csv.cc)
target_link_libraries(enb_metrics_test srsran_phy srsran_common)

View File

@ -0,0 +1,13 @@
#
# 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.
#
add_executable(ngap_test ngap_test.cc)
target_link_libraries(ngap_test srsran_common ngap_nr_asn1 srsenb_upper srsran_upper ngap_nr_asn1 srsgnb_upper srsgnb_ngap ${SCTP_LIBRARIES})
add_test(ngap_test ngap_test)

View File

@ -0,0 +1,152 @@
/**
*
* \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 "srsenb/hdr/stack/ngap/ngap.h"
#include "srsran/common/network_utils.h"
#include "srsran/common/test_common.h"
using namespace srsenb;
struct amf_dummy {
amf_dummy(const char* addr_str_, int port_) : addr_str(addr_str_), port(port_)
{
srsran::net_utils::set_sockaddr(&amf_sockaddr, addr_str, port);
{
using namespace srsran::net_utils;
fd = open_socket(addr_family::ipv4, socket_type::seqpacket, protocol_type::SCTP);
TESTASSERT(fd > 0);
TESTASSERT(bind_addr(fd, amf_sockaddr));
}
int success = listen(fd, SOMAXCONN);
srsran_assert(success == 0, "Failed to listen to incoming SCTP connections");
}
~amf_dummy()
{
if (fd > 0) {
close(fd);
}
}
srsran::unique_byte_buffer_t read_msg(sockaddr_in* sockfrom = nullptr)
{
srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer();
sockaddr_in from = {};
socklen_t fromlen = sizeof(from);
sctp_sndrcvinfo sri = {};
int flags = 0;
ssize_t n_recv = sctp_recvmsg(fd, pdu->msg, pdu->get_tailroom(), (struct sockaddr*)&from, &fromlen, &sri, &flags);
if (n_recv > 0) {
if (sockfrom != nullptr) {
*sockfrom = from;
}
pdu->N_bytes = n_recv;
}
return pdu;
}
const char* addr_str;
int port;
struct sockaddr_in amf_sockaddr = {};
int fd;
srsran::unique_byte_buffer_t last_sdu;
};
struct dummy_socket_manager : public srsran::socket_manager_itf {
dummy_socket_manager() : srsran::socket_manager_itf(srslog::fetch_basic_logger("TEST")) {}
/// Register (fd, callback). callback is called within socket thread when fd has data.
bool add_socket_handler(int fd, recv_callback_t handler) final
{
if (s1u_fd > 0) {
return false;
}
s1u_fd = fd;
callback = std::move(handler);
return true;
}
/// remove registered socket fd
bool remove_socket(int fd) final
{
if (s1u_fd < 0) {
return false;
}
s1u_fd = -1;
return true;
}
int s1u_fd;
recv_callback_t callback;
};
void run_ng_setup(ngap& ngap_obj, amf_dummy& amf)
{
asn1::ngap_nr::ngap_pdu_c ngap_pdu;
// gNB -> AMF: NG Setup Request
srsran::unique_byte_buffer_t sdu = amf.read_msg();
TESTASSERT(sdu->N_bytes > 0);
asn1::cbit_ref cbref(sdu->msg, sdu->N_bytes);
TESTASSERT(ngap_pdu.unpack(cbref) == asn1::SRSASN_SUCCESS);
TESTASSERT(ngap_pdu.type().value == asn1::ngap_nr::ngap_pdu_c::types_opts::init_msg);
TESTASSERT(ngap_pdu.init_msg().proc_code == ASN1_NGAP_NR_ID_NG_SETUP);
// AMF -> gNB: ng Setup Response
sockaddr_in amf_addr = {};
sctp_sndrcvinfo rcvinfo = {};
int flags = 0;
uint8_t ng_setup_resp[] = {0x20, 0x15, 0x00, 0x55, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x31, 0x17, 0x00, 0x61, 0x6d,
0x61, 0x72, 0x69, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x61, 0x6d, 0x66, 0x2e, 0x35, 0x67, 0x63,
0x2e, 0x6d, 0x6e, 0x63, 0x30, 0x30, 0x31, 0x2e, 0x6d, 0x63, 0x63, 0x30, 0x30, 0x31, 0x2e,
0x33, 0x67, 0x70, 0x70, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6f, 0x72, 0x67,
0x00, 0x60, 0x00, 0x08, 0x00, 0x00, 0x00, 0xf1, 0x10, 0x80, 0x01, 0x01, 0x00, 0x56, 0x40,
0x01, 0x32, 0x00, 0x50, 0x00, 0x08, 0x00, 0x00, 0xf1, 0x10, 0x00, 0x00, 0x00, 0x08};
memcpy(sdu->msg, ng_setup_resp, sizeof(ng_setup_resp));
sdu->N_bytes = sizeof(ng_setup_resp);
TESTASSERT(ngap_obj.handle_amf_rx_msg(std::move(sdu), amf_addr, rcvinfo, flags));
}
int main(int argc, char** argv)
{
// Setup logging.
auto& logger = srslog::fetch_basic_logger("NGAP");
logger.set_level(srslog::basic_levels::debug);
logger.set_hex_dump_max_size(-1);
srsran::task_scheduler task_sched;
dummy_socket_manager rx_sockets;
ngap ngap_obj(&task_sched, logger, &rx_sockets);
const char* amf_addr_str = "127.0.0.1";
const uint32_t AMF_PORT = 38412;
amf_dummy amf(amf_addr_str, AMF_PORT);
ngap_args_t args = {};
args.cell_id = 0x01;
args.gnb_id = 0x19B;
args.mcc = 907;
args.mnc = 70;
args.ngc_bind_addr = "127.0.0.100";
args.tac = 7;
args.gtp_bind_addr = "127.0.0.100";
args.amf_addr = amf_addr_str;
args.gnb_name = "srsgnb01";
rrc_interface_ngap_nr rrc;
ngap_obj.init(args, &rrc);
// Start the log backend.
srsran::test_init(argc, argv);
run_ng_setup(ngap_obj, amf);
}