Unverified Commit 34aca71c authored by Ali Güngör's avatar Ali Güngör Committed by GitHub

Merge pull request #262 from aligungr/dev

v3.1.2
parents a39a01d9 724167da
......@@ -2,19 +2,24 @@
<a href="https://github.com/aligungr/UERANSIM"><img src="/.github/logo.png" width="75" title="UERANSIM"></a>
</p>
<p align="center">
<img src="https://img.shields.io/badge/UERANSIM-v3.1.1-blue" />
<img src="https://img.shields.io/badge/UERANSIM-v3.1.2-blue" />
<img src="https://img.shields.io/badge/3GPP-R15-orange" />
<img src="https://img.shields.io/badge/License-GPL--3.0-green"/>
</p>
**UERANSIM** <small>(pronounced "ju-i ræn sɪm")</small>, is the open-source state-of-the-art 5G UE and RAN (gNodeB) implementation. It can be considered as a 5G mobile phone and a base station in basic terms. The project can be used for testing 5G Core Network and studying 5G System.
**UERANSIM** <small>(pronounced "ju-i ræn sɪm")</small>, is the open-source state-of-the-art 5G UE and RAN (gNodeB)
implementation. It can be considered as a 5G mobile phone and a base station in basic terms. The project can be used for
testing 5G Core Network and studying 5G System.
## Current Status
Our UE and gNodeB are functional and ready to use. You can connect them to your 5G core network right now and start using it.
Our UE and gNodeB are functional and ready to use. You can connect them to your 5G core network right now and start
using it.
In terms of 3GPP coverage, fundamental control plane features are done. However, some of them are in progress.
At the same time, 5G-NR radio interface is under development but not complete yet. Currently we utilize the radio interface over a simulated environment.
At the same time, 5G-NR radio interface is under development but not complete yet. Currently we utilize the radio
interface over a simulated environment.
<p align="center">
<img src="https://img.shields.io/badge/5G%20Radio%20Interface-in%20progress-orange" alt="OS Linux"/>
......@@ -23,20 +28,28 @@ At the same time, 5G-NR radio interface is under development but not complete ye
</p>
## Documentation
You can find the documentation on [UERANSIM Wiki](https://github.com/aligungr/UERANSIM/wiki).
And, since the project is rapidly developing, please make sure that you have always the [latest](https://github.com/aligungr/UERANSIM/releases) UERANSIM.
And, since the project is rapidly developing, please make sure that you have always
the [latest](https://github.com/aligungr/UERANSIM/releases) UERANSIM.
## Contributing
Implementing UE and RAN is not an easy task and is very time-consuming. We are always open to public contributions and pull requests.
Implementing UE and RAN is not an easy task and is very time-consuming. We are always open to public contributions and
pull requests.
## Supporting
UERANSIM is the first and currently only open source project that implements 5G-SA UE and RAN. Commercial alternatives of this software cost hundreds of thousands of dollars. You can support this free and open source software by:
UERANSIM is the first and currently only open source project that implements 5G-SA UE and RAN. Commercial alternatives
of this software cost hundreds of thousands of dollars. You can support this free and open source software by:
- Donating on [Open Collective](https://opencollective.com/UERANSIM)
- Starring our GitHub repository,
- Creating pull requests, submitting bugs, suggesting new features or documentation updates.
## License
Copyright (c) 2021 ALİ GÜNGÖR. All source code and related files including documentation and wiki pages are licensed under [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), see [LICENSE](https://github.com/aligungr/UERANSIM/blob/master/LICENSE) for more details.
Copyright (c) 2021 ALİ GÜNGÖR. All source code and related files including documentation and wiki pages are licensed
under [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.en.html),
see [LICENSE](https://github.com/aligungr/UERANSIM/blob/master/LICENSE) for more details.
# IMSI number of the UE. IMSI = [MCC|MNC|MSISDN] (In total 15 or 16 digits)
supi: 'imsi-286010000000001'
# Mobile Country Code value
# Mobile Country Code value of HPLMN
mcc: '286'
# Mobile Network Code value (2 or 3 digits)
# Mobile Network Code value of HPLMN (2 or 3 digits)
mnc: '93'
# Permanent subscription key
......@@ -30,16 +30,23 @@ sessions:
sst: 1
sd: 1
# List of requested S-NSSAIs by this UE
slices:
# Configured NSSAI for this UE by HPLMN
configured-nssai:
- sst: 1
sd: 1
# Supported encryption and integrity algorithms by this UE
# Default Configured NSSAI for this UE
default-nssai:
- sst: 1
sd: 1
# Supported encryption algorithms by this UE
integrity:
IA1: true
IA2: true
IA3: true
# Supported integrity algorithms by this UE
ciphering:
EA1: true
EA2: true
......
# IMSI number of the UE. IMSI = [MCC|MNC|MSISDN] (In total 15 or 16 digits)
supi: 'imsi-208930000000003'
# Mobile Country Code value
# Mobile Country Code value of HPLMN
mcc: '208'
# Mobile Network Code value (2 or 3 digits)
# Mobile Network Code value of HPLMN (2 or 3 digits)
mnc: '93'
# Permanent subscription key
......@@ -10,7 +10,7 @@ key: '8baf473f2f8fd09487cccbd7097c6862'
# Operator code (OP or OPC) of the UE
op: '8e27b6af0e692e750f32667a3b14605d'
# This value specifies the OP type and it can be either 'OP' or 'OPC'
opType: 'OP'
opType: 'OPC'
# Authentication Management Field (AMF) value
amf: '8000'
# IMEI number of the device. It is used if no SUPI is provided
......@@ -30,16 +30,23 @@ sessions:
sst: 0x01
sd: 0x010203
# List of requested S-NSSAIs by this UE
slices:
# Configured NSSAI for this UE by HPLMN
configured-nssai:
- sst: 0x01
sd: 0x010203
# Supported encryption and integrity algorithms by this UE
# Default Configured NSSAI for this UE
default-nssai:
- sst: 1
sd: 1
# Supported encryption algorithms by this UE
integrity:
IA1: true
IA2: true
IA3: true
# Supported integrity algorithms by this UE
ciphering:
EA1: true
EA2: true
......
# IMSI number of the UE. IMSI = [MCC|MNC|MSISDN] (In total 15 or 16 digits)
supi: 'imsi-901700000000003'
# Mobile Country Code value
supi: 'imsi-901700000000001'
# Mobile Country Code value of HPLMN
mcc: '901'
# Mobile Network Code value (2 or 3 digits)
# Mobile Network Code value of HPLMN (2 or 3 digits)
mnc: '70'
# Permanent subscription key
......@@ -10,7 +10,7 @@ key: '465B5CE8B199B49FAA5F0A2EE238A6BC'
# Operator code (OP or OPC) of the UE
op: 'E8ED289DEBA952E4283B54E88E6183CA'
# This value specifies the OP type and it can be either 'OP' or 'OPC'
opType: 'OP'
opType: 'OPC'
# Authentication Management Field (AMF) value
amf: '8000'
# IMEI number of the device. It is used if no SUPI is provided
......@@ -30,16 +30,23 @@ sessions:
sst: 1
sd: 1
# List of requested S-NSSAIs by this UE
slices:
# Configured NSSAI for this UE by HPLMN
configured-nssai:
- sst: 1
sd: 1
# Supported encryption and integrity algorithms by this UE
# Default Configured NSSAI for this UE
default-nssai:
- sst: 1
sd: 1
# Supported encryption algorithms by this UE
integrity:
IA1: true
IA2: true
IA3: true
# Supported integrity algorithms by this UE
ciphering:
EA1: true
EA2: true
......
......@@ -65,11 +65,11 @@ static nr::gnb::GnbConfig *ReadConfigYaml()
for (auto &nssai : yaml::GetSequence(config, "slices"))
{
SliceSupport s{};
SingleSlice s{};
s.sst = yaml::GetInt32(nssai, "sst", 1, 0xFF);
if (yaml::HasField(nssai, "sd"))
s.sd = octet3{yaml::GetInt32(nssai, "sd", 1, 0xFFFFFF)};
result->nssais.push_back(s);
result->nssai.slices.push_back(s);
}
return result;
......
......@@ -106,7 +106,7 @@ void GnbMrTask::onLoop()
break;
}
case NwGnbRrcToMr::AN_RELEASE: {
m_rlsEntity->localReleaseConnection(w->ueId, rls::ECause::RRC_RELEASE);
m_rlsEntity->localReleaseConnection(w->ueId, rls::ECause::RRC_NORMAL_RELEASE);
break;
}
}
......
......@@ -23,12 +23,12 @@
#include <asn/ngap/ASN_NGAP_InitiatingMessage.h>
#include <asn/ngap/ASN_NGAP_NGAP-PDU.h>
#include <asn/ngap/ASN_NGAP_NGSetupRequest.h>
#include <asn/ngap/ASN_NGAP_OverloadStartNSSAIItem.h>
#include <asn/ngap/ASN_NGAP_PLMNSupportItem.h>
#include <asn/ngap/ASN_NGAP_ProtocolIE-Field.h>
#include <asn/ngap/ASN_NGAP_ServedGUAMIItem.h>
#include <asn/ngap/ASN_NGAP_SliceSupportItem.h>
#include <asn/ngap/ASN_NGAP_SupportedTAItem.h>
#include <asn/ngap/ASN_NGAP_OverloadStartNSSAIItem.h>
namespace nr::gnb
{
......@@ -67,7 +67,7 @@ static void AssignDefaultAmfConfigs(NgapAmfContext *amf, T *msg)
auto plmnSupport = new PlmnSupport();
ngap_utils::PlmnFromAsn_Ref(item.pLMNIdentity, plmnSupport->plmn);
asn::ForeachItem(item.sliceSupportList, [plmnSupport](ASN_NGAP_SliceSupportItem &ssItem) {
plmnSupport->sliceSupportList.push_back(ngap_utils::SliceSupportFromAsn_Unique(ssItem));
plmnSupport->sliceSupportList.slices.push_back(ngap_utils::SliceSupportFromAsn(ssItem));
});
amf->plmnSupportList.push_back(plmnSupport);
});
......@@ -139,7 +139,7 @@ void NgapTask::sendNgSetupRequest(int amfId)
auto *broadcastPlmn = asn::New<ASN_NGAP_BroadcastPLMNItem>();
asn::SetOctetString3(broadcastPlmn->pLMNIdentity, ngap_utils::PlmnToOctet3(m_base->config->plmn));
for (auto &nssai : m_base->config->nssais)
for (auto &nssai : m_base->config->nssai.slices)
{
auto *item = asn::New<ASN_NGAP_SliceSupportItem>();
asn::SetOctetString1(item->s_NSSAI.sST, static_cast<uint8_t>(nssai.sst));
......
......@@ -192,10 +192,9 @@ void NgapTask::receiveSessionResourceSetupRequest(int amfId, ASN_NGAP_PDUSession
sendNgapUeAssociated(ue->ctxId, respPdu);
if (failedList.empty())
m_logger->info("PDU session resource is established for UE[%d] count[%d]", ue->ctxId, successList.size());
m_logger->info("PDU session resource is setup for UE[%d] count[%d]", ue->ctxId, successList.size());
else if (successList.empty())
m_logger->err("PDU session resource establishment was failed for UE[%d] count[%d]", ue->ctxId,
failedList.size());
m_logger->err("PDU session resource setup was failed for UE[%d] count[%d]", ue->ctxId, failedList.size());
else
m_logger->err("PDU session establishment is partially successful for UE[%d], success[%d], failed[%d]",
successList.size(), failedList.size());
......
......@@ -86,13 +86,13 @@ void GuamiFromAsn_Ref(const ASN_NGAP_GUAMI_t &guami, Guami &target)
PlmnFromAsn_Ref(guami.pLMNIdentity, target.plmn);
}
std::unique_ptr<SliceSupport> SliceSupportFromAsn_Unique(ASN_NGAP_SliceSupportItem &supportItem)
SingleSlice SliceSupportFromAsn(ASN_NGAP_SliceSupportItem &supportItem)
{
auto s = std::make_unique<SliceSupport>();
s->sst = asn::GetOctet1(supportItem.s_NSSAI.sST);
s->sd = std::nullopt;
SingleSlice s{};
s.sst = asn::GetOctet1(supportItem.s_NSSAI.sST);
s.sd = std::nullopt;
if (supportItem.s_NSSAI.sD)
s->sd = asn::GetOctet3(*supportItem.s_NSSAI.sD);
s.sd = asn::GetOctet3(*supportItem.s_NSSAI.sD);
return s;
}
......
......@@ -35,7 +35,7 @@ void GuamiFromAsn_Ref(const ASN_NGAP_GUAMI_t &guami, Guami &target);
void ToCauseAsn_Ref(NgapCause source, ASN_NGAP_Cause_t &target);
void ToPlmnAsn_Ref(const Plmn &source, ASN_NGAP_PLMNIdentity_t &target);
std::unique_ptr<SliceSupport> SliceSupportFromAsn_Unique(ASN_NGAP_SliceSupportItem &supportItem);
SingleSlice SliceSupportFromAsn(ASN_NGAP_SliceSupportItem &supportItem);
NgapIdPair FindNgapIdPairFromAsnNgapIds(const ASN_NGAP_UE_NGAP_IDs &ngapIDs);
......
......@@ -26,7 +26,7 @@ Json ToJson(const GnbConfig &v)
{"nci", v.nci},
{"plmn", ToJson(v.plmn)},
{"tac", v.tac},
{"nssai", ToJson(v.nssais)},
{"nssai", ToJson(v.nssai)},
{"ngap-ip", v.ngapIp},
{"gtp-ip", v.gtpIp},
{"paging-drx", ToJson(v.pagingDrx)},
......
......@@ -289,7 +289,7 @@ struct GnbConfig
int gnbIdLength{}; // 22..32 bit
Plmn plmn{};
int tac{};
std::vector<SliceSupport> nssais{};
NetworkSlice nssai{};
std::vector<GnbAmfConfig> amfConfigs{};
std::string portalIp{};
std::string ngapIp{};
......
......@@ -150,8 +150,8 @@ struct IENasKeySetIdentifier : InformationElement1
struct IENetworkSlicingIndication : InformationElement1
{
ENetworkSlicingSubscriptionChangeIndication nssci{};
EDefaultConfiguredNssaiIndication dcni{};
ENetworkSlicingSubscriptionChangeIndication nssci{}; // This is spare if dir is UE->NW
EDefaultConfiguredNssaiIndication dcni{}; // This is spare if dir is NW->UE
IENetworkSlicingIndication() = default;
IENetworkSlicingIndication(ENetworkSlicingSubscriptionChangeIndication nssci,
......
......@@ -8,6 +8,7 @@
#include "timer.hpp"
#include <sstream>
#include <utils/common.hpp>
namespace nas
......@@ -96,16 +97,16 @@ bool NasTimer::performTick()
if (running)
{
long currentMs = utils::CurrentTimeMillis();
long deltaSec = (currentMs - startMillis) / 1000;
long deltaSec = (currentMs - startMillis) / 1000LL;
long remainingSec = interval - deltaSec;
if (currentMs - _lastDebugPrintMs > 10 * 1000)
if (currentMs - _lastDebugPrintMs > 10LL * 1000LL)
{
_lastDebugPrintMs = currentMs;
// Log.debug(Tag.TIMER, "NAS Timer %s int:%ss rem:%ss", timerCode, interval, remainingSec);
}
if (remainingSec < 0)
if (remainingSec <= 0LL)
{
stop(false);
expiryCount++;
......@@ -125,8 +126,8 @@ int NasTimer::getRemaining() const
if (!running)
return 0;
long elapsed = utils::CurrentTimeMillis() - startMillis;
return static_cast<int>(std::max(interval - elapsed, 0L));
int elapsed = static_cast<int>((utils::CurrentTimeMillis() - startMillis) / 1000LL);
return std::max(interval - elapsed, 0);
}
void NasTimer::resetExpiryCount()
......@@ -141,13 +142,13 @@ int NasTimer::getExpiryCount() const
Json ToJson(const NasTimer &v)
{
int interval = v.getInterval();
std::stringstream ss{};
if (v.isRunning())
ss << "rem[" << v.getRemaining() << "] int[" << v.getInterval() << "]";
else
ss << ".";
return Json::Obj({
{"interval", interval == INT32_MAX ? Json{"inf"} : interval},
{"remaining", v.getRemaining()},
{"running", v.isRunning()},
});
return ss.str();
}
} // namespace nas
......@@ -13,7 +13,7 @@
namespace nas::utils
{
IESNssai SNssaiFrom(const SliceSupport &v)
IESNssai SNssaiFrom(const SingleSlice &v)
{
IESNssai r;
r.sst = v.sst;
......@@ -22,10 +22,10 @@ IESNssai SNssaiFrom(const SliceSupport &v)
return r;
}
IENssai NssaiFrom(const std::vector<SliceSupport> &v)
IENssai NssaiFrom(const NetworkSlice &v)
{
IENssai r;
for (auto &x : v)
for (auto &x : v.slices)
r.sNssais.push_back(SNssaiFrom(x));
return r;
}
......@@ -262,4 +262,31 @@ IEDnn DnnFromApn(const std::string &apn)
return dnn;
}
void AddToPlmnList(IEPlmnList &list, VPlmn item)
{
if (!std::any_of(list.plmns.begin(), list.plmns.end(), [&item](auto &i) { return DeepEqualsV(i, item); }))
list.plmns.push_back(item);
}
VPlmn PlmnFrom(const Plmn &plmn)
{
return VPlmn{plmn.mcc, plmn.mnc, plmn.isLongMnc};
}
NetworkSlice NssaiTo(const IENssai &v)
{
NetworkSlice nssai{};
for (auto &item : v.sNssais)
nssai.slices.push_back(SNssaiTo(item));
return nssai;
}
SingleSlice SNssaiTo(const IESNssai &v)
{
SingleSlice sNssai{};
sNssai.sst = v.sst;
sNssai.sd = v.sd;
return sNssai;
}
} // namespace nas::utils
......@@ -13,13 +13,19 @@
namespace nas::utils
{
IESNssai SNssaiFrom(const SliceSupport &v);
IENssai NssaiFrom(const std::vector<SliceSupport> &v);
IEDnn DnnFromApn(const std::string& apn);
IESNssai SNssaiFrom(const SingleSlice &v);
IENssai NssaiFrom(const NetworkSlice &v);
IEDnn DnnFromApn(const std::string &apn);
VPlmn PlmnFrom(const Plmn &plmn);
NetworkSlice NssaiTo(const IENssai &v);
SingleSlice SNssaiTo(const IESNssai &v);
bool HasValue(const IEGprsTimer3 &v);
bool HasValue(const IEGprsTimer2 &v);
void AddToPlmnList(IEPlmnList &list, VPlmn item);
const char *EnumToString(ERegistrationType v);
const char *EnumToString(EMmCause v);
const char *EnumToString(ESmCause v);
......@@ -43,6 +49,15 @@ inline bool DeepEqualsIe(const T &a, const T &b)
return s1 == s2;
}
template <typename T>
inline bool DeepEqualsV(const T &a, const T &b)
{
OctetString s1{}, s2{};
T::Encode(a, s1);
T::Encode(b, s2);
return s1 == s2;
}
template <typename T>
inline T DeepCopyIe(const T &a)
{
......
......@@ -160,7 +160,7 @@ void VTime::Encode(const VTime &value, OctetString &stream)
VTime VTime::Decode(const OctetView &stream)
{
VTime time;
VTime time{};
time.year = stream.read();
time.month = stream.read();
time.day = stream.read();
......@@ -178,28 +178,21 @@ VTime::VTime(const octet &year, const octet &month, const octet &day, const octe
void VRejectedSNssai::Encode(const VRejectedSNssai &value, OctetString &stream)
{
int totalLength = 0;
if (value.sd.has_value() && !value.sst.has_value())
{
// error: "sst must not be null if sd is not null" (currently ignoring)
}
if (value.sst.has_value())
totalLength++;
int totalLength = 1;
if (value.sd.has_value())
totalLength += 3;
int octet = totalLength << 4 | static_cast<int>(value.cause);
stream.appendOctet(octet);
if (value.sst.has_value())
stream.appendOctet(value.sst.value());
stream.appendOctet(value.sst);
if (value.sd.has_value())
stream.appendOctet3(value.sd.value());
}
VRejectedSNssai VRejectedSNssai::Decode(const OctetView &stream)
{
VRejectedSNssai res;
VRejectedSNssai res{};
int octet = stream.readI();
res.cause = static_cast<ERejectedSNssaiCause>(octet & 0xF);
......@@ -212,8 +205,7 @@ VRejectedSNssai VRejectedSNssai::Decode(const OctetView &stream)
return res;
}
VRejectedSNssai::VRejectedSNssai(ERejectedSNssaiCause cause, const std::optional<octet> &sst,
const std::optional<octet3> &sd)
VRejectedSNssai::VRejectedSNssai(ERejectedSNssaiCause cause, octet sst, const std::optional<octet3> &sd)
: cause(cause), sst(sst), sd(sd)
{
}
......@@ -243,7 +235,7 @@ VPartialServiceAreaList VPartialServiceAreaList::Decode(const OctetView &stream)
{
auto octet = stream.peek();
VPartialServiceAreaList res;
VPartialServiceAreaList res{};
res.present = bits::BitRange8<5, 6>(octet);
switch (res.present)
{
......
......@@ -98,12 +98,12 @@ struct VTime
struct VRejectedSNssai
{
ERejectedSNssaiCause cause{};
std::optional<octet> sst{};
octet sst{};
std::optional<octet3> sd{};
VRejectedSNssai() = default;
VRejectedSNssai(ERejectedSNssaiCause cause, const std::optional<octet> &sst, const std::optional<octet3> &sd);
VRejectedSNssai(ERejectedSNssaiCause cause, octet sst, const std::optional<octet3> &sd);
static void Encode(const VRejectedSNssai &value, OctetString &stream);
static VRejectedSNssai Decode(const OctetView &stream);
......
......@@ -99,28 +99,42 @@ static nr::ue::UeConfig *ReadConfigYaml()
auto *result = new nr::ue::UeConfig();
auto config = YAML::LoadFile(g_options.configFile);
result->plmn.mcc = yaml::GetInt32(config, "mcc", 1, 999);
result->hplmn.mcc = yaml::GetInt32(config, "mcc", 1, 999);
yaml::GetString(config, "mcc", 3, 3);
result->plmn.mnc = yaml::GetInt32(config, "mnc", 0, 999);
result->plmn.isLongMnc = yaml::GetString(config, "mnc", 2, 3).size() == 3;
result->hplmn.mnc = yaml::GetInt32(config, "mnc", 0, 999);
result->hplmn.isLongMnc = yaml::GetString(config, "mnc", 2, 3).size() == 3;
for (auto &gnbSearchItem : yaml::GetSequence(config, "gnbSearchList"))
result->gnbSearchList.push_back(gnbSearchItem.as<std::string>());
for (auto &nssai : yaml::GetSequence(config, "slices"))
if (yaml::HasField(config, "default-nssai"))
{
SliceSupport s{};
s.sst = yaml::GetInt32(nssai, "sst", 1, 0xFF);
if (yaml::HasField(nssai, "sd"))
s.sd = octet3{yaml::GetInt32(nssai, "sd", 1, 0xFFFFFF)};
result->nssais.push_back(s);
for (auto &sNssai : yaml::GetSequence(config, "default-nssai"))
{
SingleSlice s{};
s.sst = yaml::GetInt32(sNssai, "sst", 1, 0xFF);
if (yaml::HasField(sNssai, "sd"))
s.sd = octet3{yaml::GetInt32(sNssai, "sd", 1, 0xFFFFFF)};
result->initials.defaultConfiguredNssai.slices.push_back(s);
}
}
if (yaml::HasField(config, "configured-nssai"))
{
for (auto &sNssai : yaml::GetSequence(config, "configured-nssai"))
{
SingleSlice s{};
s.sst = yaml::GetInt32(sNssai, "sst", 1, 0xFF);
if (yaml::HasField(sNssai, "sd"))
s.sd = octet3{yaml::GetInt32(sNssai, "sd", 1, 0xFFFFFF)};
result->initials.configuredNssai.slices.push_back(s);
}
}
result->key = OctetString::FromHex(yaml::GetString(config, "key", 32, 32));
result->opC = OctetString::FromHex(yaml::GetString(config, "op", 32, 32));
result->amf = OctetString::FromHex(yaml::GetString(config, "amf", 4, 4));
result->autoBehaviour = true;
result->configureRouting = !g_options.noRoutingConfigs;
// If we have multiple UEs in the same process, then log names should be separated.
......@@ -162,7 +176,7 @@ static nr::ue::UeConfig *ReadConfigYaml()
if (yaml::HasField(sess, "slice"))
{
auto slice = sess["slice"];
s.sNssai = SliceSupport{};
s.sNssai = SingleSlice{};
s.sNssai->sst = yaml::GetInt32(slice, "sst", 1, 0xFF);
if (yaml::HasField(slice, "sd"))
s.sNssai->sd = octet3{yaml::GetInt32(slice, "sd", 1, 0xFFFFFF)};
......@@ -275,7 +289,6 @@ static void IncrementNumber(std::string &s, int delta)
static nr::ue::UeConfig *GetConfigByUe(int ueIndex)
{
auto *c = new nr::ue::UeConfig();
c->autoBehaviour = g_refConfig->autoBehaviour;
c->key = g_refConfig->key.copy();
c->opC = g_refConfig->opC.copy();
c->opType = g_refConfig->opType;
......@@ -283,8 +296,8 @@ static nr::ue::UeConfig *GetConfigByUe(int ueIndex)
c->imei = g_refConfig->imei;
c->imeiSv = g_refConfig->imeiSv;
c->supi = g_refConfig->supi;
c->plmn = g_refConfig->plmn;
c->nssais = g_refConfig->nssais;
c->hplmn = g_refConfig->hplmn;
c->initials = g_refConfig->initials;
c->supportedAlgs = g_refConfig->supportedAlgs;
c->gnbSearchList = g_refConfig->gnbSearchList;
c->initSessions = g_refConfig->initSessions;
......
......@@ -109,9 +109,11 @@ void UeCmdHandler::handleCmdImpl(NwUeCliCommand &msg)
{"cm-state", ToJson(m_base->nasTask->mm->m_cmState)},
{"rm-state", ToJson(m_base->nasTask->mm->m_rmState)},
{"mm-state", ToJson(m_base->nasTask->mm->m_mmSubState)},
{"sim-inserted", m_base->nasTask->mm->m_validSim},
{"stored-suci", ToJson(m_base->nasTask->mm->m_storedSuci)},
{"stored-guti", ToJson(m_base->nasTask->mm->m_storedGuti)},
{"5u-state", ToJson(m_base->nasTask->mm->m_storage.m_uState)},
{"sim-inserted", m_base->nasTask->mm->m_storage.isSimValid()},
{"stored-suci", ToJson(m_base->nasTask->mm->m_storage.m_storedSuci)},
{"stored-guti", ToJson(m_base->nasTask->mm->m_storage.m_storedGuti)},
{"has-emergency", ::ToJson(m_base->nasTask->mm->hasEmergency())},
{"pdu-sessions", Json::Arr(std::move(pduSessions))},
});
sendResult(msg.address, json.dumpYaml());
......@@ -127,8 +129,8 @@ void UeCmdHandler::handleCmdImpl(NwUeCliCommand &msg)
}
case app::UeCliCommand::DE_REGISTER: {
m_base->nasTask->mm->sendDeregistration(msg.cmd->isSwitchOff ? nas::ESwitchOff::SWITCH_OFF
: nas::ESwitchOff::NORMAL_DE_REGISTRATION,
msg.cmd->dueToDisable5g);
: nas::ESwitchOff::NORMAL_DE_REGISTRATION,
msg.cmd->dueToDisable5g);
if (!msg.cmd->isSwitchOff)
sendResult(msg.address, "De-registration procedure triggered");
else
......
This diff is collapsed.
......@@ -25,9 +25,10 @@ NasMm::NasMm(TaskBase *base, UeTimers *timers) : m_base{base}, m_timers{timers},
m_cmState = ECmState::CM_IDLE;
m_mmState = EMmState::MM_DEREGISTERED;
m_mmSubState = EMmSubState::MM_DEREGISTERED_NA;
m_uState = E5UState::U1_UPDATED;
m_autoBehaviour = base->config->autoBehaviour;
m_validSim = base->config->supi.has_value();
m_storage.m_uState = E5UState::U1_UPDATED;
m_storage.initialize(base->config->supi.has_value(), base->config->initials);
m_storage.m_currentPlmn = base->config->hplmn; // TODO: normally assigned after plmn search
}
void NasMm::onStart(NasSm *sm)
......@@ -52,7 +53,7 @@ void NasMm::performMmCycle()
if (m_mmSubState == EMmSubState::MM_DEREGISTERED_NA)
{
if (m_validSim)
if (m_storage.isSimValid())
{
if (m_cmState == ECmState::CM_IDLE)
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_PLMN_SEARCH);
......@@ -81,8 +82,8 @@ void NasMm::performMmCycle()
if (m_mmSubState == EMmSubState::MM_DEREGISTERED_NORMAL_SERVICE)
{
if (m_autoBehaviour && !m_timers->t3346.isRunning())
sendRegistration(nas::ERegistrationType::INITIAL_REGISTRATION, nas::EFollowOnRequest::FOR_PENDING);
if (!m_timers->t3346.isRunning())
sendRegistration(nas::ERegistrationType::INITIAL_REGISTRATION, false);
return;
}
......@@ -96,12 +97,6 @@ void NasMm::performMmCycle()
return;
if (m_mmSubState == EMmSubState::MM_DEREGISTERED_NO_SUPI)
return;
if (m_autoBehaviour)
{
m_logger->err("unhandled UE MM state");
return;
}
}
void NasMm::switchMmState(EMmState state, EMmSubState subState)
......@@ -168,15 +163,15 @@ void NasMm::switchCmState(ECmState state)
void NasMm::switchUState(E5UState state)
{
E5UState oldState = m_uState;
m_uState = state;
E5UState oldState = m_storage.m_uState;
m_storage.m_uState = state;
onSwitchUState(oldState, m_uState);
onSwitchUState(oldState, m_storage.m_uState);
if (m_base->nodeListener)
{
m_base->nodeListener->onSwitch(app::NodeType::UE, m_base->config->getNodeName(), app::StateType::U5,
ToJson(oldState).str(), ToJson(m_uState).str());
ToJson(oldState).str(), ToJson(m_storage.m_uState).str());
}
if (state != oldState)
......@@ -192,12 +187,12 @@ void NasMm::onSwitchMmState(EMmState oldState, EMmState newState, EMmSubState ol
// 5GMM-DEREGISTERED for any other state except 5GMM-NULL.
if (oldState == EMmState::MM_DEREGISTERED && newState != EMmState::MM_DEREGISTERED && newState != EMmState::MM_NULL)
{
if (m_currentNsCtx.has_value() || m_nonCurrentNsCtx.has_value())
if (m_storage.m_currentNsCtx || m_storage.m_nonCurrentNsCtx)
{
m_logger->debug("Deleting NAS security context");
m_currentNsCtx = {};
m_nonCurrentNsCtx = {};
m_storage.m_currentNsCtx = {};
m_storage.m_nonCurrentNsCtx = {};
}
}
}
......@@ -210,8 +205,21 @@ void NasMm::onSwitchCmState(ECmState oldState, ECmState newState)
{
if (oldState == ECmState::CM_CONNECTED && newState == ECmState::CM_IDLE)
{
// 5.5.1.2.7 Abnormal cases in the UE (in registration)
if (m_mmState == EMmState::MM_REGISTERED_INITIATED)
{
// e) Lower layer failure or release of the NAS signalling connection received from lower layers before the
// REGISTRATION ACCEPT or REGISTRATION REJECT message is received. The UE shall abort the registration
// procedure for initial registration and proceed as ...
switchRmState(ERmState::RM_DEREGISTERED);
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
switchUState(E5UState::U2_NOT_UPDATED);
handleCommonAbnormalRegFailure(m_lastRegistrationRequest->registrationType.registrationType);
}
// 5.5.2.2.6 Abnormal cases in the UE (in de-registration)
if (m_mmState == EMmState::MM_DEREGISTERED_INITIATED)
else if (m_mmState == EMmState::MM_DEREGISTERED_INITIATED)
{
// The de-registration procedure shall be aborted and the UE proceeds as follows:
// if the de-registration procedure was performed due to disabling of 5GS services, the UE shall enter the
......@@ -224,7 +232,8 @@ void NasMm::onSwitchCmState(ECmState oldState, ECmState newState)
nas::ESwitchOff::NORMAL_DE_REGISTRATION)
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
m_lastDeregistrationRequest = nullptr;
switchRmState(ERmState::RM_DEREGISTERED);
m_lastDeregDueToDisable5g = false;
}
}
......@@ -234,69 +243,25 @@ void NasMm::onSwitchUState(E5UState oldState, E5UState newState)
{
}
void NasMm::onTimerExpire(nas::NasTimer &timer)
void NasMm::setN1Capability(bool enabled)
{
switch (timer.getCode())
{
case 3346: {
if (m_autoBehaviour && m_mmSubState == EMmSubState::MM_DEREGISTERED_NORMAL_SERVICE)
{
sendRegistration(nas::ERegistrationType::INITIAL_REGISTRATION, nas::EFollowOnRequest::FOR_PENDING);
}
break;
}
case 3512: {
if (m_autoBehaviour && m_mmState == EMmState::MM_REGISTERED && m_cmState == ECmState::CM_CONNECTED)
{
sendRegistration(nas::ERegistrationType::PERIODIC_REGISTRATION_UPDATING,
nas::EFollowOnRequest::FOR_PENDING);
}
break;
}
case 3521: {
if (timer.getExpiryCount() == 5)
{
timer.resetExpiryCount();
if (m_mmState == EMmState::MM_DEREGISTERED_INITIATED && m_lastDeregistrationRequest != nullptr)
{
m_logger->debug("De-registration aborted");
if (m_lastDeregDueToDisable5g)
switchMmState(EMmState::MM_NULL, EMmSubState::MM_NULL_NA);
else if (m_lastDeregistrationRequest->deRegistrationType.switchOff ==
nas::ESwitchOff::NORMAL_DE_REGISTRATION)
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
}
}
else
{
if (m_mmState == EMmState::MM_DEREGISTERED_INITIATED && m_lastDeregistrationRequest != nullptr)
{
m_logger->debug("Retrying de-registration request");
sendNasMessage(*m_lastDeregistrationRequest);
m_timers->t3521.start(false);
}
}
break;
}
}
}
void NasMm::invalidateAcquiredParams()
{
m_storedGuti = {};
m_lastVisitedRegisteredTai = {};
m_taiList = {};
m_currentNsCtx = {};
m_nonCurrentNsCtx = {};
// TODO
}
void NasMm::invalidateSim()
bool NasMm::hasEmergency()
{
m_logger->warn("USIM is removed or invalidated");
m_validSim = false;
invalidateAcquiredParams();
// Indicates emergency services are required (even if registered for normal initial registration)
// This happens if it 'has' or 'need' some emergency PDU Session, as well.
if (m_rmState == ERmState::RM_REGISTERED && m_registeredForEmergency)
return true;
if (m_mmState == EMmState::MM_REGISTERED_INITIATED && m_lastRegistrationRequest &&
m_lastRegistrationRequest->registrationType.registrationType == nas::ERegistrationType::EMERGENCY_REGISTRATION)
return true;
// TODO: Other case which is an emergency PDU session is established, or need to be established (and wanted to be
// established soon)
return false;
}
} // namespace nr::ue
......@@ -17,13 +17,13 @@ void NasMm::receiveConfigurationUpdate(const nas::ConfigurationUpdateCommand &ms
if (msg.guti.has_value() && msg.guti->type == nas::EIdentityType::GUTI)
{
m_storedGuti = msg.guti.value();
m_storedSuci = {};
m_storage.m_storedSuci = {};
m_storage.m_storedGuti = *msg.guti;
m_timers->t3519.stop();
}
if (msg.taiList.has_value())
m_taiList = msg.taiList.value();
m_storage.m_taiList = msg.taiList.value();
if (msg.configurationUpdateIndication.has_value())
{
......
......@@ -30,10 +30,10 @@ void NasMm::sendDeregistration(nas::ESwitchOff switchOff, bool dueToDisable5g)
request->deRegistrationType.reRegistrationRequired = nas::EReRegistrationRequired::NOT_REQUIRED;
request->deRegistrationType.switchOff = switchOff;
if (m_currentNsCtx.has_value())
if (m_storage.m_currentNsCtx)
{
request->ngKSI.tsc = m_currentNsCtx->tsc;
request->ngKSI.ksi = m_currentNsCtx->ngKsi;
request->ngKSI.tsc = m_storage.m_currentNsCtx->tsc;
request->ngKSI.ksi = m_storage.m_currentNsCtx->ngKsi;
}
else
{
......@@ -76,7 +76,7 @@ void NasMm::receiveDeregistrationAccept(const nas::DeRegistrationAcceptUeOrigina
m_timers->t3521.stop();
m_timers->t3519.stop();
m_storedSuci = {};
m_storage.m_storedSuci = {};
switchRmState(ERmState::RM_DEREGISTERED);
......@@ -85,7 +85,6 @@ void NasMm::receiveDeregistrationAccept(const nas::DeRegistrationAcceptUeOrigina
else
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
m_lastDeregistrationRequest = nullptr;
m_lastDeregDueToDisable5g = false;
m_logger->info("De-registration is successful");
......@@ -109,9 +108,16 @@ void NasMm::receiveDeregistrationRequest(const nas::DeRegistrationRequestUeTermi
m_logger->debug("Network initiated de-registration request received");
bool forceIgnoreReregistration = false;
bool forceLocalReleaseNas = false;
// 5.5.1.2.7 Abnormal cases in the UE (de-registration collision)
if (m_mmState == EMmState::MM_REGISTERED_INITIATED)
{
if (msg.deRegistrationType.reRegistrationRequired == nas::EReRegistrationRequired::REQUIRED)
forceLocalReleaseNas = true;
}
// 5.5.2.2.6 Abnormal cases in the UE (de-registration collision)
if (m_mmState == EMmState::MM_DEREGISTERED_INITIATED)
else if (m_mmState == EMmState::MM_DEREGISTERED_INITIATED)
{
// De-registration containing de-registration type "switch off", If the UE receives a DEREGISTRATION REQUEST
// message before the UE-initiated de-registration procedure has been completed, this message shall be ignored
......@@ -162,7 +168,12 @@ void NasMm::receiveDeregistrationRequest(const nas::DeRegistrationRequestUeTermi
case nas::EMmCause::ILLEGAL_ME:
case nas::EMmCause::FIVEG_SERVICES_NOT_ALLOWED: {
switchUState(E5UState::U3_ROAMING_NOT_ALLOWED);
invalidateSim();
m_storage.m_storedGuti = {};
m_storage.m_lastVisitedRegisteredTai = {};
m_storage.m_taiList = {};
m_storage.m_currentNsCtx = {};
m_storage.m_nonCurrentNsCtx = {};
m_storage.invalidateSim();
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
break;
}
......@@ -175,13 +186,21 @@ void NasMm::receiveDeregistrationRequest(const nas::DeRegistrationRequestUeTermi
//}
case nas::EMmCause::TA_NOT_ALLOWED: {
switchUState(E5UState::U3_ROAMING_NOT_ALLOWED);
invalidateAcquiredParams();
m_storage.m_storedGuti = {};
m_storage.m_lastVisitedRegisteredTai = {};
m_storage.m_taiList = {};
m_storage.m_currentNsCtx = {};
m_storage.m_nonCurrentNsCtx = {};
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_LIMITED_SERVICE);
break;
}
case nas::EMmCause::N1_MODE_NOT_ALLOWED: {
switchUState(E5UState::U3_ROAMING_NOT_ALLOWED);
invalidateAcquiredParams();
m_storage.m_storedGuti = {};
m_storage.m_lastVisitedRegisteredTai = {};
m_storage.m_taiList = {};
m_storage.m_currentNsCtx = {};
m_storage.m_nonCurrentNsCtx = {};
switchMmState(EMmState::MM_NULL, EMmSubState::MM_NULL_NA);
break;
}
......@@ -198,13 +217,25 @@ void NasMm::receiveDeregistrationRequest(const nas::DeRegistrationRequestUeTermi
nas::utils::EnumToString(msg.mmCause->value));
switchUState(E5UState::U3_ROAMING_NOT_ALLOWED);
invalidateSim();
m_storage.m_storedGuti = {};
m_storage.m_lastVisitedRegisteredTai = {};
m_storage.m_taiList = {};
m_storage.m_currentNsCtx = {};
m_storage.m_nonCurrentNsCtx = {};
m_storage.invalidateSim();
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
break;
}
}
}
}
if (reRegistrationRequired)
{
// TODO: Perform re-registration, but consider forceLocalReleaseNas
// if 'forceLocalReleaseNas' is true, local release nas before re-registration.
// See "5.5.1.2.7 g) De-registration procedure collision."
}
}
} // namespace nr::ue
......@@ -42,22 +42,21 @@ void NasMm::receiveIdentityRequest(const nas::IdentityRequest &msg)
nas::IE5gsMobileIdentity NasMm::getOrGenerateSuci()
{
if (m_timers->t3519.isRunning())
return m_storedSuci;
m_storedSuci = generateSuci();
if (m_timers->t3519.isRunning() && m_storage.m_storedSuci.type != nas::EIdentityType::NO_IDENTITY)
return m_storage.m_storedSuci;
m_storage.m_storedSuci = generateSuci();
m_timers->t3519.start();
if (m_storedSuci.type == nas::EIdentityType::NO_IDENTITY)
if (m_storage.m_storedSuci.type == nas::EIdentityType::NO_IDENTITY)
return {};
return m_storedSuci;
return m_storage.m_storedSuci;
}
nas::IE5gsMobileIdentity NasMm::generateSuci()
{
auto &supi = m_base->config->supi;
auto &plmn = m_base->config->plmn;
auto &plmn = m_storage.m_currentPlmn;
if (!supi.has_value())
return {};
......@@ -69,14 +68,6 @@ nas::IE5gsMobileIdentity NasMm::generateSuci()
}
const std::string &imsi = supi->value;
int mccInImsi = utils::ParseInt(imsi.substr(0, 3));
int mncInImsi = utils::ParseInt(imsi.substr(3, plmn.isLongMnc ? 3 : 2));
if (mccInImsi != plmn.mcc || mncInImsi != plmn.mnc)
{
m_logger->err("MCC/MNC mismatch in SUCI generation.");
return {};
}
nas::IE5gsMobileIdentity ret;
ret.type = nas::EIdentityType::SUCI;
......@@ -93,35 +84,33 @@ nas::IE5gsMobileIdentity NasMm::generateSuci()
nas::IE5gsMobileIdentity NasMm::getOrGeneratePreferredId()
{
if (m_storedGuti.type != nas::EIdentityType::NO_IDENTITY)
return m_storedGuti;
if (m_storage.m_storedGuti.type != nas::EIdentityType::NO_IDENTITY)
return m_storage.m_storedGuti;
auto suci = getOrGenerateSuci();
if (suci.type != nas::EIdentityType::NO_IDENTITY)
{
return suci;
}
else if (m_base->config->imei.has_value())
{
nas::IE5gsMobileIdentity res{};
res.type = nas::EIdentityType::IMEI;
res.value = *m_base->config->imei;
return res;
}
else if (m_base->config->imeiSv.has_value())
{
nas::IE5gsMobileIdentity res{};
res.type = nas::EIdentityType::IMEISV;
res.value = *m_base->config->imeiSv;
return res;
}
else
{
auto suci = getOrGenerateSuci();
if (suci.type != nas::EIdentityType::NO_IDENTITY)
{
return suci;
}
else if (m_base->config->imei.has_value())
{
nas::IE5gsMobileIdentity res{};
res.type = nas::EIdentityType::IMEI;
res.value = *m_base->config->imei;
return res;
}
else if (m_base->config->imeiSv.has_value())
{
nas::IE5gsMobileIdentity res{};
res.type = nas::EIdentityType::IMEISV;
res.value = *m_base->config->imeiSv;
return res;
}
else
{
nas::IE5gsMobileIdentity res{};
res.type = nas::EIdentityType::NO_IDENTITY;
return res;
}
nas::IE5gsMobileIdentity res{};
res.type = nas::EIdentityType::NO_IDENTITY;
return res;
}
}
......
......@@ -11,6 +11,7 @@
#include <crypt/milenage.hpp>
#include <nas/nas.hpp>
#include <nas/timer.hpp>
#include <ue/nas/storage.hpp>
#include <ue/nts.hpp>
#include <ue/types.hpp>
#include <utils/nts.hpp>
......@@ -28,31 +29,27 @@ class NasMm
UeTimers *m_timers;
std::unique_ptr<Logger> m_logger;
NasSm *m_sm;
MobileStorage m_storage{};
ERmState m_rmState;
ECmState m_cmState;
EMmState m_mmState;
EMmSubState m_mmSubState;
E5UState m_uState;
nas::IE5gsMobileIdentity m_storedSuci{};
nas::IE5gsMobileIdentity m_storedGuti{};
// Most recent registration request
std::unique_ptr<nas::RegistrationRequest> m_lastRegistrationRequest{};
// Most recent de-registration request
std::unique_ptr<nas::DeRegistrationRequestUeOriginating> m_lastDeregistrationRequest{};
// Indicates that the last de-registration request is issued due to disable 5G services
bool m_lastDeregDueToDisable5g{};
std::optional<nas::IE5gsTrackingAreaIdentity> m_lastVisitedRegisteredTai{};
std::optional<nas::IE5gsTrackingAreaIdentityList> m_taiList{};
std::optional<NasSecurityContext> m_currentNsCtx;
std::optional<NasSecurityContext> m_nonCurrentNsCtx;
bool m_autoBehaviour;
bool m_validSim;
// Last time PLMN search is triggered
long m_lastPlmnSearchTrigger{};
OctetString m_sqn{};
// Registration attempt counter
int m_regCounter{};
// Indicates registered for emergency services (Only meaningful in RM-REGISTERED state)
bool m_registeredForEmergency{};
// Network feature support information
nas::IE5gsNetworkFeatureSupport m_nwFeatureSupport{};
friend class UeCmdHandler;
......@@ -65,9 +62,8 @@ class NasMm
void onQuit();
void triggerMmCycle();
void performMmCycle();
void onTimerExpire(nas::NasTimer &timer);
/* Radio resource control */
/* Radio */
void handlePlmnSearchResponse(const std::string &gnbName);
void handlePlmnSearchFailure();
void handleRrcConnectionSetup();
......@@ -81,6 +77,9 @@ class NasMm
/* De-registration */
void sendDeregistration(nas::ESwitchOff switchOff, bool dueToDisable5g);
/* Timer */
void onTimerExpire(nas::NasTimer &timer);
private:
/* Base */
void switchMmState(EMmState state, EMmSubState subState);
......@@ -91,8 +90,8 @@ class NasMm
void onSwitchRmState(ERmState oldState, ERmState newState);
void onSwitchCmState(ECmState oldState, ECmState newState);
void onSwitchUState(E5UState oldState, E5UState newState);
void invalidateAcquiredParams();
void invalidateSim();
void setN1Capability(bool enabled);
bool hasEmergency();
/* Transport */
void sendMmStatus(nas::EMmCause cause);
......@@ -102,9 +101,10 @@ class NasMm
void receiveMmCause(const nas::IE5gMmCause &msg);
/* Registration */
void sendRegistration(nas::ERegistrationType registrationType, nas::EFollowOnRequest followOn);
void sendRegistration(nas::ERegistrationType regType, bool dueToDereg);
void receiveRegistrationAccept(const nas::RegistrationAccept &msg);
void receiveRegistrationReject(const nas::RegistrationReject &msg);
void handleCommonAbnormalRegFailure(nas::ERegistrationType regType);
/* Authentication */
void receiveAuthenticationRequest(const nas::AuthenticationRequest &msg);
......@@ -140,6 +140,13 @@ class NasMm
/* Service */
void receiveServiceAccept(const nas::ServiceAccept &msg);
void receiveServiceReject(const nas::ServiceReject &msg);
/* Network Slicing */
NetworkSlice makeRequestedNssai(bool &isDefaultNssai) const;
void handleNetworkSlicingSubscriptionChange();
/* Radio */
void localReleaseConnection();
};
} // namespace nr::ue
\ No newline at end of file
......@@ -9,6 +9,7 @@
#include "mm.hpp"
#include <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/rrc/task.hpp>
#include <ue/sm/sm.hpp>
namespace nr::ue
......@@ -54,4 +55,11 @@ void NasMm::handleRadioLinkFailure()
handleRrcConnectionRelease();
}
void NasMm::localReleaseConnection()
{
m_logger->info("Performing local release of NAS connection");
m_base->rrcTask->push(new NwUeNasToRrc(NwUeNasToRrc::LOCAL_RELEASE_CONNECTION));
}
} // namespace nr::ue
\ No newline at end of file
This diff is collapsed.
......@@ -13,8 +13,26 @@
namespace nr::ue
{
static bool IsValidKsi(const nas::IENasKeySetIdentifier &ngKsi)
{
return ngKsi.tsc == nas::ETypeOfSecurityContext::NATIVE_SECURITY_CONTEXT &&
ngKsi.ksi != nas::IENasKeySetIdentifier::NOT_AVAILABLE_OR_RESERVED;
}
static int FindSecurityContext(int ksi, const std::unique_ptr<NasSecurityContext> &current,
const std::unique_ptr<NasSecurityContext> &nonCurrent)
{
if (current != nullptr && current->ngKsi == ksi)
return 0;
if (nonCurrent != nullptr && nonCurrent->ngKsi == ksi)
return 1;
return -1;
}
void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
{
m_logger->debug("Security Mode Command received");
auto reject = [this](nas::EMmCause cause) {
nas::SecurityModeReject resp;
resp.mmCause.value = cause;
......@@ -22,66 +40,111 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
m_logger->err("Rejecting Security Mode Command with cause: %s", nas::utils::EnumToString(cause));
};
if (!m_nonCurrentNsCtx.has_value())
// ============================== Check the received ngKSI ==============================
if (!IsValidKsi(msg.ngKsi))
{
reject(nas::EMmCause::MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE);
m_logger->err("Invalid ngKSI received, tsc[%d], ksi[%d]", (int)msg.ngKsi.tsc, msg.ngKsi.ksi);
reject(nas::EMmCause::SEC_MODE_REJECTED_UNSPECIFIED);
return;
}
// TODO: check the integrity with new security context
if (msg.ngKsi.ksi == 0 &&
msg.selectedNasSecurityAlgorithms.integrity == nas::ETypeOfIntegrityProtectionAlgorithm::IA0 &&
msg.selectedNasSecurityAlgorithms.ciphering == nas::ETypeOfCipheringAlgorithm::EA0)
{
// TODO
}
int whichCtx = FindSecurityContext(msg.ngKsi.ksi, m_storage.m_currentNsCtx, m_storage.m_nonCurrentNsCtx);
if (whichCtx == -1)
{
m_logger->err("Security context with ngKSI[%d] not found", msg.ngKsi.ksi);
reject(nas::EMmCause::SEC_MODE_REJECTED_UNSPECIFIED);
return;
}
auto &nsCtx = whichCtx == 0 ? m_storage.m_currentNsCtx : m_storage.m_nonCurrentNsCtx;
// ======================== Check the integrity with new security context ========================
{
// TODO:
octet4 mac = msg._macForNewSC;
(void)mac;
}
// Check replayed UE security capabilities
// ======================== Check replayed UE security capabilities ========================
if (!nas::utils::DeepEqualsIe(msg.replayedUeSecurityCapabilities, createSecurityCapabilityIe()))
{
m_logger->err("Replayed UE security capability mismatch");
reject(nas::EMmCause::UE_SECURITY_CAP_MISMATCH);
return;
}
// ======================== Check selected NAS security algorithms ========================
{
auto &replayed = msg.replayedUeSecurityCapabilities;
auto real = createSecurityCapabilityIe();
auto integrity = msg.selectedNasSecurityAlgorithms.integrity;
auto ciphering = msg.selectedNasSecurityAlgorithms.ciphering;
if (!nas::utils::DeepEqualsIe(replayed, real))
if (integrity > nas::ETypeOfIntegrityProtectionAlgorithm::IA3_128 ||
ciphering > nas::ETypeOfCipheringAlgorithm::EA3_128)
{
m_logger->err("Selected NAS security algorithms are invalid");
reject(nas::EMmCause::UE_SECURITY_CAP_MISMATCH);
return;
}
}
// Handle EAP-Success message if any.
if (msg.eapMessage.has_value())
{
if (msg.eapMessage->eap->code == eap::ECode::SUCCESS)
receiveEapSuccessMessage(*msg.eapMessage->eap);
else
m_logger->warn(
"EAP message with inconvenient code received in Security Mode Command. Ignoring EAP message.");
}
// ============================ Process the security context. ============================
// Assign ABBA (if any)
if (msg.abba.has_value())
m_nonCurrentNsCtx->keys.abba = msg.abba->rawData.copy();
nsCtx->keys.abba = msg.abba->rawData.copy();
// Check selected algorithms
// Assign selected algorithms to security context, and derive NAS keys
nsCtx->integrity = msg.selectedNasSecurityAlgorithms.integrity;
nsCtx->ciphering = msg.selectedNasSecurityAlgorithms.ciphering;
keys::DeriveNasKeys(*nsCtx);
m_logger->debug("Derived kNasEnc[%s] kNasInt[%s]", nsCtx->keys.kNasEnc.toHexString().c_str(),
nsCtx->keys.kNasInt.toHexString().c_str());
m_logger->debug("Selected integrity[%d] ciphering[%d]", (int)nsCtx->integrity, (int)nsCtx->ciphering);
// The UE shall in addition reset the uplink NAS COUNT counter if a) the SECURITY MODE COMMAND message is received
// in order to take a 5G NAS security context into use created after a successful execution of the 5G AKA based
// primary authentication and key agreement procedure or the EAP based ...
if (whichCtx == 1) // It is unclear how we can detect this, but checking if it is 'non-current' one.
{
nsCtx->uplinkCount.sqn = 0;
nsCtx->uplinkCount.overflow = octet2{0};
}
if (msg.selectedNasSecurityAlgorithms.integrity != nas::ETypeOfIntegrityProtectionAlgorithm::IA0)
{
// TODO
// if (msg.selectedNasSecurityAlgorithms.integrity is supported according to config file)
// if (msg.selectedNasSecurityAlgorithms.ciphering is supported according to config file)
}
// Assign selected algorithms to security context, and derive NAS keys
m_nonCurrentNsCtx->integrity = msg.selectedNasSecurityAlgorithms.integrity;
m_nonCurrentNsCtx->ciphering = msg.selectedNasSecurityAlgorithms.ciphering;
keys::DeriveNasKeys(*m_nonCurrentNsCtx);
// Set the new NAS Security Context as current one. (If it is not already the current one)
if (whichCtx == 1)
m_storage.m_currentNsCtx = std::make_unique<NasSecurityContext>(nsCtx->deepCopy());
// ============================ Handle EAP-Success message if any. ============================
m_logger->debug("Derived kNasEnc[%s] kNasInt[%s]", m_nonCurrentNsCtx->keys.kNasEnc.toHexString().c_str(),
m_nonCurrentNsCtx->keys.kNasInt.toHexString().c_str());
m_logger->debug("Selected integrity[%d] ciphering[%d]", (int)m_nonCurrentNsCtx->integrity,
(int)m_nonCurrentNsCtx->ciphering);
if (msg.eapMessage.has_value())
{
if (msg.eapMessage->eap->code == eap::ECode::SUCCESS)
receiveEapSuccessMessage(*msg.eapMessage->eap);
else
m_logger->warn(
"EAP message with inconvenient code received in Security Mode Command. Ignoring EAP message.");
}
// Set non-current NAS Security Context as current one.
m_currentNsCtx = m_nonCurrentNsCtx->deepCopy();
// ============================ Send the Security Mode Complete. ============================
// Prepare response
nas::SecurityModeComplete resp;
nas::SecurityModeComplete resp{};
// Append IMEISV if requested
if (msg.imeiSvRequest.has_value() && msg.imeiSvRequest->imeiSvRequest == nas::EImeiSvRequest::REQUESTED)
......@@ -94,6 +157,8 @@ void NasMm::receiveSecurityModeCommand(const nas::SecurityModeCommand &msg)
}
}
// TODO: Bu service request de olabilir en son hangisiyse, ayrıca son mesaj yerine son unciphered mesaj da olabilir
// See 4.4.6
resp.nasMessageContainer = nas::IENasMessageContainer{};
nas::EncodeNasMessage(*m_lastRegistrationRequest, resp.nasMessageContainer->data);
......@@ -106,7 +171,7 @@ nas::IEUeSecurityCapability NasMm::createSecurityCapabilityIe()
auto &algs = m_base->config->supportedAlgs;
auto supported = ~0;
nas::IEUeSecurityCapability res;
nas::IEUeSecurityCapability res{};
res.b_5G_EA0 = supported;
res.b_5G_IA0 = supported;
res.b_EEA0 = supported;
......
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "mm.hpp"
#include <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/sm/sm.hpp>
namespace nr::ue
{
static bool ContainsSNssai(const NetworkSlice &nssai, const SingleSlice &sNssai)
{
return std::any_of(nssai.slices.begin(), nssai.slices.end(), [&sNssai](auto &i) { return i == sNssai; });
}
static void AppendSubset(const NetworkSlice &source, NetworkSlice &target, const NetworkSlice &rejectedInPlmn,
const NetworkSlice &rejectedInTa, size_t maxSNssai)
{
size_t appended = 0;
for (auto &slice : source.slices)
{
if (appended == maxSNssai)
break;
if (ContainsSNssai(rejectedInPlmn, slice))
continue;
if (ContainsSNssai(rejectedInTa, slice))
continue;
target.slices.push_back(slice);
appended++;
}
}
NetworkSlice NasMm::makeRequestedNssai(bool &isDefaultNssai) const
{
isDefaultNssai = false;
NetworkSlice res{};
if (!m_storage.m_allowedNssai.slices.empty() || !m_storage.m_configuredNssai.slices.empty())
{
if (!m_storage.m_allowedNssai.slices.empty())
{
AppendSubset(m_storage.m_allowedNssai, res, m_storage.m_rejectedNssaiInPlmn, m_storage.m_rejectedNssaiInTa,
8);
AppendSubset(m_storage.m_configuredNssai, res, m_storage.m_rejectedNssaiInPlmn,
m_storage.m_rejectedNssaiInTa, static_cast<size_t>(8) - res.slices.size());
}
else
{
AppendSubset(m_storage.m_configuredNssai, res, m_storage.m_rejectedNssaiInPlmn,
m_storage.m_rejectedNssaiInTa, 8);
}
}
else if (!m_storage.m_defConfiguredNssai.slices.empty())
{
AppendSubset(m_storage.m_defConfiguredNssai, res, m_storage.m_rejectedNssaiInPlmn,
m_storage.m_rejectedNssaiInTa, 8);
isDefaultNssai = true;
}
return res;
}
void NasMm::handleNetworkSlicingSubscriptionChange()
{
// TODO
}
} // namespace nr::ue
\ No newline at end of file
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "mm.hpp"
#include <nas/utils.hpp>
#include <ue/app/task.hpp>
#include <ue/nas/task.hpp>
#include <ue/rrc/task.hpp>
#include <utils/common.hpp>
namespace nr::ue
{
void NasMm::onTimerExpire(nas::NasTimer &timer)
{
auto logExpired = [this, &timer]() {
m_logger->debug("NAS timer[%d] expired [%d]", timer.getCode(), timer.getExpiryCount());
};
switch (timer.getCode())
{
case 3346: {
if (m_mmSubState == EMmSubState::MM_DEREGISTERED_NORMAL_SERVICE)
{
logExpired();
sendRegistration(nas::ERegistrationType::INITIAL_REGISTRATION, false);
}
break;
}
case 3510: {
// The UE shall abort the registration procedure for initial registration and the NAS signalling connection, if
// any, shall be released locally if the initial registration request is not for emergency services..
if (m_mmState == EMmState::MM_REGISTERED_INITIATED && m_lastRegistrationRequest)
{
logExpired();
switchRmState(ERmState::RM_DEREGISTERED);
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
switchUState(E5UState::U2_NOT_UPDATED);
if (m_lastRegistrationRequest->registrationType.registrationType !=
nas::ERegistrationType::EMERGENCY_REGISTRATION)
{
localReleaseConnection();
}
handleCommonAbnormalRegFailure(m_lastRegistrationRequest->registrationType.registrationType);
}
break;
}
case 3512: {
if (m_mmState == EMmState::MM_REGISTERED && m_cmState == ECmState::CM_CONNECTED)
{
logExpired();
sendRegistration(nas::ERegistrationType::PERIODIC_REGISTRATION_UPDATING, false);
}
break;
}
case 3519: {
m_storage.m_storedSuci = {};
break;
}
case 3521: {
if (timer.getExpiryCount() == 5)
{
timer.resetExpiryCount();
if (m_mmState == EMmState::MM_DEREGISTERED_INITIATED && m_lastDeregistrationRequest != nullptr)
{
logExpired();
m_logger->debug("De-registration aborted");
if (m_lastDeregDueToDisable5g)
switchMmState(EMmState::MM_NULL, EMmSubState::MM_NULL_NA);
else if (m_lastDeregistrationRequest->deRegistrationType.switchOff ==
nas::ESwitchOff::NORMAL_DE_REGISTRATION)
switchMmState(EMmState::MM_DEREGISTERED, EMmSubState::MM_DEREGISTERED_NA);
}
}
else
{
if (m_mmState == EMmState::MM_DEREGISTERED_INITIATED && m_lastDeregistrationRequest != nullptr)
{
logExpired();
m_logger->debug("Retrying de-registration request");
sendNasMessage(*m_lastDeregistrationRequest);
m_timers->t3521.start(false);
}
}
break;
}
}
}
} // namespace nr::ue
......@@ -21,10 +21,11 @@ void NasMm::sendNasMessage(const nas::PlainMmMessage &msg)
// TODO trigger on send
OctetString pdu{};
if (m_currentNsCtx.has_value() && (m_currentNsCtx->integrity != nas::ETypeOfIntegrityProtectionAlgorithm::IA0 ||
m_currentNsCtx->ciphering != nas::ETypeOfCipheringAlgorithm::EA0))
if (m_storage.m_currentNsCtx &&
(m_storage.m_currentNsCtx->integrity != nas::ETypeOfIntegrityProtectionAlgorithm::IA0 ||
m_storage.m_currentNsCtx->ciphering != nas::ETypeOfCipheringAlgorithm::EA0))
{
auto secured = nas_enc::Encrypt(*m_currentNsCtx, msg);
auto secured = nas_enc::Encrypt(*m_storage.m_currentNsCtx, msg);
nas::EncodeNasMessage(*secured, pdu);
}
else
......@@ -62,7 +63,7 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
{
// If any NAS signalling message is received as not integrity protected even though the secure exchange of NAS
// messages has been established by the network, then the NAS shall discard this message
if (m_currentNsCtx.has_value())
if (m_storage.m_currentNsCtx)
{
m_logger->err(
"Not integrity protected NAS message received after security establishment. Ignoring received "
......@@ -100,14 +101,14 @@ void NasMm::receiveNasMessage(const nas::NasMessage &msg)
return;
}
if (!m_currentNsCtx.has_value())
if (!m_storage.m_currentNsCtx)
{
m_logger->warn("Secured NAS message received while no security context");
sendMmStatus(nas::EMmCause::MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE);
return;
}
auto decrypted = nas_enc::Decrypt(*m_currentNsCtx, securedMm);
auto decrypted = nas_enc::Decrypt(*m_storage.m_currentNsCtx, securedMm);
if (decrypted == nullptr)
{
m_logger->err("MAC mismatch in NAS encryption. Ignoring received NAS Message.");
......
......@@ -122,7 +122,7 @@ void UeMrTask::onLoop()
break;
}
case NwUeRrcToMr::RRC_CONNECTION_RELEASE: {
m_rlsEntity->localReleaseConnection(rls::ECause::RRC_RELEASE);
m_rlsEntity->localReleaseConnection(w->cause);
m_rlsEntity->resetEntity();
break;
}
......
......@@ -19,10 +19,10 @@ static const int N_UP_int_alg = 0x06;
namespace nr::ue::keys
{
void DeriveKeysSeafAmf(const UeConfig &ueConfig, NasSecurityContext &nasSecurityContext)
void DeriveKeysSeafAmf(const UeConfig &ueConfig, const Plmn& currentPlmn, NasSecurityContext &nasSecurityContext)
{
auto &keys = nasSecurityContext.keys;
std::string snn = ConstructServingNetworkName(ueConfig.plmn);
std::string snn = ConstructServingNetworkName(currentPlmn);
OctetString s1[1];
s1[0] = crypto::EncodeKdfString(snn);
......
......@@ -16,7 +16,7 @@ namespace nr::ue::keys
/**
* Derives SEAF and AMF keys
*/
void DeriveKeysSeafAmf(const UeConfig &ueConfig, NasSecurityContext &nasSecurityContext);
void DeriveKeysSeafAmf(const UeConfig &ueConfig, const Plmn &currentPlmn, NasSecurityContext &nasSecurityContext);
/**
* Derives NAS keys
......
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include "storage.hpp"
namespace nr::ue
{
} // namespace nr::ue
//
// This file is a part of UERANSIM open source project.
// Copyright (c) 2021 ALİ GÜNGÖR.
//
// The software and all associated files are licensed under GPL-3.0
// and subject to the terms and conditions defined in LICENSE file.
//
#include <nas/nas.hpp>
#include <ue/types.hpp>
#pragma once
namespace nr::ue
{
class MobileStorage
{
private:
bool m_simIsValid{};
public:
// Location related
nas::IE5gsMobileIdentity m_storedGuti{};
std::optional<nas::IE5gsTrackingAreaIdentity> m_lastVisitedRegisteredTai{};
E5UState m_uState{};
// Identity related
nas::IE5gsMobileIdentity m_storedSuci{};
// Plmn related
Plmn m_currentPlmn{};
nas::IE5gsTrackingAreaIdentityList m_taiList{};
nas::IE5gsTrackingAreaIdentityList m_forbiddenTaiList{};
nas::IEPlmnList m_equivalentPlmnList{};
nas::IEPlmnList m_forbiddenPlmnList{};
nas::IEServiceAreaList m_serviceAreaList{};
// Security related
std::unique_ptr<NasSecurityContext> m_currentNsCtx{};
std::unique_ptr<NasSecurityContext> m_nonCurrentNsCtx{};
OctetString m_sqn{};
// NSSAI related
NetworkSlice m_defConfiguredNssai{};
NetworkSlice m_configuredNssai{};
NetworkSlice m_allowedNssai{};
NetworkSlice m_rejectedNssaiInPlmn{};
NetworkSlice m_rejectedNssaiInTa{};
public:
void initialize(bool hasSupi, const UeConfig::Initials &initials)
{
m_simIsValid = hasSupi;
m_defConfiguredNssai = initials.defaultConfiguredNssai;
m_configuredNssai = initials.configuredNssai;
}
void invalidateSim()
{
m_simIsValid = false;
}
[[nodiscard]] bool isSimValid() const
{
return m_simIsValid;
}
};
} // namespace nr::ue
\ No newline at end of file
......@@ -141,8 +141,6 @@ void NasTask::onTimerExpire(nas::NasTimer &timer)
void NasTask::performTick()
{
auto sendExpireMsg = [this](nas::NasTimer *timer) {
logger->debug("NAS timer[%d] expired [%d]", timer->getCode(), timer->getExpiryCount());
auto *nw = new NwUeNasToNas(NwUeNasToNas::NAS_TIMER_EXPIRE);
nw->timer = timer;
push(nw);
......
......@@ -177,6 +177,7 @@ struct NwUeNasToRrc : NtsMessage
enum PR
{
PLMN_SEARCH_REQUEST,
LOCAL_RELEASE_CONNECTION,
INITIAL_NAS_DELIVERY,
UPLINK_NAS_DELIVERY
} present;
......@@ -206,6 +207,9 @@ struct NwUeRrcToMr : NtsMessage
rrc::RrcChannel channel{};
OctetString pdu{};
// RRC_CONNECTION_RELEASE
rls::ECause cause{};
explicit NwUeRrcToMr(PR present) : NtsMessage(NtsMessageType::UE_RRC_TO_MR), present(present)
{
}
......
......@@ -130,7 +130,10 @@ void UeRrcTask::receiveRrcRelease(const ASN_RRC_RRCRelease &msg)
m_logger->debug("RRC Release received");
m_state = ERrcState::RRC_IDLE;
m_base->mrTask->push(new NwUeRrcToMr(NwUeRrcToMr::RRC_CONNECTION_RELEASE));
auto *wr = new NwUeRrcToMr(NwUeRrcToMr::RRC_CONNECTION_RELEASE);
wr->cause = rls::ECause::RRC_NORMAL_RELEASE;
m_base->mrTask->push(wr);
m_base->nasTask->push(new NwUeRrcToNas(NwUeRrcToNas::RRC_CONNECTION_RELEASE));
}
......
......@@ -84,6 +84,15 @@ void UeRrcTask::onLoop()
case NwUeNasToRrc::UPLINK_NAS_DELIVERY:
deliverUplinkNas(std::move(w->nasPdu));
break;
case NwUeNasToRrc::LOCAL_RELEASE_CONNECTION:
m_state = ERrcState::RRC_IDLE;
auto *wr = new NwUeRrcToMr(NwUeRrcToMr::RRC_CONNECTION_RELEASE);
wr->cause = rls::ECause::RRC_LOCAL_RELEASE;
m_base->mrTask->push(wr);
m_base->nasTask->push(new NwUeRrcToNas(NwUeRrcToNas::RRC_CONNECTION_RELEASE));
break;
}
break;
}
......
......@@ -33,15 +33,13 @@ void NasSm::sendEstablishmentRequest(const SessionConfig &config)
return;
}
// TODO
nas::ProtocolConfigurationOptions opt;
nas::ProtocolConfigurationOptions opt{};
opt.additionalParams.push_back(std::make_unique<nas::ProtocolConfigurationItem>(
nas::EProtocolConfigId::CONT_ID_UP_IP_ADDRESS_ALLOCATION_VIA_NAS_SIGNALLING, true, OctetString::Empty()));
opt.additionalParams.push_back(std::make_unique<nas::ProtocolConfigurationItem>(
nas::EProtocolConfigId::CONT_ID_DOWN_DNS_SERVER_IPV4_ADDRESS, true, OctetString::Empty()));
// TODO
nas::PduSessionEstablishmentRequest req;
nas::PduSessionEstablishmentRequest req{};
req.pti = pti;
req.pduSessionId = static_cast<nas::EPduSessionIdentity>(psi);
req.integrityProtectionMaximumDataRate.maxRateUplink =
......@@ -58,7 +56,7 @@ void NasSm::sendEstablishmentRequest(const SessionConfig &config)
req.extendedProtocolConfigurationOptions->options = opt.encode();
m_timers->t3580.start();
sendSmMessage(pti, req);
sendSmMessage(psi, req);
}
void NasSm::receivePduSessionEstablishmentAccept(const nas::PduSessionEstablishmentAccept &msg)
......
......@@ -124,10 +124,9 @@ Json ToJson(const UeConfig &v)
{
return Json::Obj({
{"supi", ToJson(v.supi)},
{"plmn", ToJson(v.plmn)},
{"hplmn", ToJson(v.hplmn)},
{"imei", ::ToJson(v.imei)},
{"imeiSv", ::ToJson(v.imeiSv)},
{"nssai", ::ToJson(v.nssais)},
});
}
......
......@@ -47,7 +47,7 @@ enum class OpType
struct SessionConfig
{
nas::EPduSessionType type{};
std::optional<SliceSupport> sNssai{};
std::optional<SingleSlice> sNssai{};
std::optional<std::string> apn{};
};
......@@ -55,20 +55,26 @@ struct UeConfig
{
/* Read from config file */
std::optional<Supi> supi{};
Plmn plmn{};
Plmn hplmn{};
OctetString key{};
OctetString opC{};
OpType opType{};
OctetString amf{};
std::optional<std::string> imei{};
std::optional<std::string> imeiSv{};
std::vector<SliceSupport> nssais{};
SupportedAlgs supportedAlgs{};
std::vector<std::string> gnbSearchList{};
std::vector<SessionConfig> initSessions{};
/* Read from config file as well, but should be stored in non-volatile
* mobile storage and subject to change in runtime */
struct Initials
{
NetworkSlice defaultConfiguredNssai{};
NetworkSlice configuredNssai{};
} initials{};
/* Assigned by program */
bool autoBehaviour{};
bool configureRouting{};
bool prefixLogger{};
......@@ -120,9 +126,9 @@ struct UeTimers
nas::NasTimer t3444; /* MM - ... */
nas::NasTimer t3445; /* MM - ... */
nas::NasTimer t3502; /* MM - ... */
nas::NasTimer t3502; /* MM - Initiation of the registration procedure, if still required */
nas::NasTimer t3510; /* MM - Registration Request transmission timer */
nas::NasTimer t3511; /* MM - ... */
nas::NasTimer t3511; /* MM - Retransmission of the REGISTRATION REQUEST, if still required */
nas::NasTimer t3512; /* MM - Periodic registration update timer */
nas::NasTimer t3516; /* MM - 5G AKA - RAND and RES* storing timer */
nas::NasTimer t3517; /* MM - Service Request transmission timer */
......@@ -226,7 +232,7 @@ struct PduSession
nas::EPduSessionType sessionType{};
std::optional<std::string> apn{};
std::optional<SliceSupport> sNssai{};
std::optional<SingleSlice> sNssai{};
std::optional<nas::IEQoSRules> authorizedQoSRules{};
std::optional<nas::IESessionAmbr> sessionAmbr{};
......
......@@ -95,8 +95,10 @@ const char *CauseToString(ECause cause)
return "RLS-SETUP-TIMEOUT";
case ECause::HEARTBEAT_TIMEOUT:
return "RLS-HEARTBEAT-TIMEOUT";
case ECause::RRC_RELEASE:
return "RLS-RRC-RELEASE";
case ECause::RRC_NORMAL_RELEASE:
return "RLS-RRC-NORMAL-RELEASE";
case ECause::RRC_LOCAL_RELEASE:
return "RLS-RRC-LOCAL-RELEASE";
default:
return "?";
}
......
......@@ -53,13 +53,14 @@ enum class ECause : uint8_t
HEARTBEAT_TIMEOUT,
// Successful causes
RRC_RELEASE,
RRC_NORMAL_RELEASE, // release with UE-gNB coordination over RRC
RRC_LOCAL_RELEASE, // release locally without UE-gNB coordination
};
// Checks if the cause treated as radio link failure
inline bool IsRlf(ECause cause)
{
return cause != ECause::RRC_RELEASE;
return cause != ECause::RRC_NORMAL_RELEASE && cause != ECause::RRC_LOCAL_RELEASE;
}
enum class EPayloadType : uint8_t
......
......@@ -113,6 +113,12 @@ void RlsUeEntity::startGnbSearch()
void RlsUeEntity::onReceive(const InetAddress &address, const OctetString &pdu)
{
if (ueToken == 0)
{
logWarn("Received PDU ignored, UE entity is not initialized");
return;
}
RlsMessage msg{};
auto res = Decode(OctetView{pdu}, msg, AppVersion);
......
......@@ -12,6 +12,7 @@
#include "octet.hpp"
#include "octet_string.hpp"
#include "time_stamp.hpp"
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
......@@ -46,6 +47,12 @@ inline void ClearAndDelete(std::vector<T *> &vector)
vector.clear();
}
template <typename T, typename P>
inline void EraseWhere(std::vector<T> &vector, P predicate)
{
vector.erase(std::remove_if(vector.begin(), vector.end(), std::forward<P>(predicate)), vector.end());
}
template <typename T>
static std::string IntToHex(T i)
{
......
......@@ -7,6 +7,7 @@
//
#include "common_types.hpp"
#include <algorithm>
#include <iomanip>
#include <sstream>
......@@ -38,12 +39,34 @@ Json ToJson(const Plmn &v)
return ss.str();
}
Json ToJson(const SliceSupport &v)
Json ToJson(const SingleSlice &v)
{
return Json::Obj({{"sst", ToJson(v.sst)}, {"sd", ToJson(v.sd)}});
}
Json ToJson(const NetworkSlice &v)
{
return ToJson(v.slices);
}
Json ToJson(const PlmnSupport &v)
{
return Json::Obj({{"plmn", ToJson(v.plmn)}, {"nssai", ToJson(v.sliceSupportList)}});
}
bool operator==(const SingleSlice &lhs, const SingleSlice &rhs)
{
if ((int)lhs.sst != (int)rhs.sst)
return false;
if (lhs.sd.has_value() != rhs.sd.has_value())
return false;
if (!lhs.sd.has_value())
return true;
return ((int)*lhs.sd) == ((int)*rhs.sd);
}
void NetworkSlice::addIfNotExists(const SingleSlice &slice)
{
if (!std::any_of(slices.begin(), slices.end(), [&slice](auto &s) { return s == slice; }))
slices.push_back(slice);
}
......@@ -30,12 +30,19 @@ struct Plmn
bool isLongMnc{};
};
struct SliceSupport
struct SingleSlice
{
octet sst{};
std::optional<octet3> sd{};
};
struct NetworkSlice
{
std::vector<SingleSlice> slices{};
void addIfNotExists(const SingleSlice &slice);
};
enum class PduSessionType
{
IPv4,
......@@ -48,7 +55,7 @@ enum class PduSessionType
struct PlmnSupport
{
Plmn plmn{};
std::vector<std::unique_ptr<SliceSupport>> sliceSupportList{};
NetworkSlice sliceSupportList{};
};
struct GutiMobileIdentity
......@@ -104,5 +111,8 @@ struct Supi
Json ToJson(const Supi &v);
Json ToJson(const Plmn &v);
Json ToJson(const SliceSupport &v);
Json ToJson(const SingleSlice &v);
Json ToJson(const NetworkSlice &v);
Json ToJson(const PlmnSupport &v);
bool operator==(const SingleSlice &lhs, const SingleSlice &rhs);
......@@ -15,10 +15,10 @@ struct cons
// Version information
static constexpr const uint8_t Major = 3;
static constexpr const uint8_t Minor = 1;
static constexpr const uint8_t Patch = 1;
static constexpr const uint8_t Patch = 2;
static constexpr const char *Project = "UERANSIM";
static constexpr const char *Tag = "v3.1.1";
static constexpr const char *Name = "UERANSIM v3.1.1";
static constexpr const char *Tag = "v3.1.2";
static constexpr const char *Name = "UERANSIM v3.1.2";
static constexpr const char *Owner = "ALİ GÜNGÖR";
// Some port values
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment