Commit 2334f6f5 authored by Robert Schmidt's avatar Robert Schmidt

Merge remote-tracking branch 'origin/nas-issue-881' into integration_2025_w07 (!3176)

Refactor and extend NAS Registration Request

Extended NAS Registration Request generation and encoding according to
8.2.6 of 3GPP TS 24.501. Main topics:

- NAS Registration Type
- NAS KSI
- integrity protection
- NAS container encoding

Improvements:

- Add 5GMM Modes in NAS
- Introduced 5GMM state machine

Fixes:

- add 5GMM Capability IE to Registration Request
- closes #881, #819
parents bab13b80 bd429488
...@@ -1106,7 +1106,8 @@ static void rrc_ue_generate_RRCSetupComplete(const NR_UE_RRC_INST_t *rrc, const ...@@ -1106,7 +1106,8 @@ static void rrc_ue_generate_RRCSetupComplete(const NR_UE_RRC_INST_t *rrc, const
if (IS_SA_MODE(get_softmodem_params())) { if (IS_SA_MODE(get_softmodem_params())) {
as_nas_info_t initialNasMsg; as_nas_info_t initialNasMsg;
nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id); nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id);
generateRegistrationRequest(&initialNasMsg, nas); // Send Initial NAS message (Registration Request) before Security Mode control procedure
generateRegistrationRequest(&initialNasMsg, nas, false);
nas_msg = (char *)initialNasMsg.nas_data; nas_msg = (char *)initialNasMsg.nas_data;
nas_msg_length = initialNasMsg.length; nas_msg_length = initialNasMsg.length;
} else { } else {
......
...@@ -35,16 +35,55 @@ ...@@ -35,16 +35,55 @@
#include "TLVDecoder.h" #include "TLVDecoder.h"
#include "FGMMCapability.h" #include "FGMMCapability.h"
int encode_5gmm_capability(const FGMMCapability *fgmmcapability, uint8_t iei, uint8_t *buffer, uint32_t len) #define MIN_LENGTH_5GMM_CAPABILITY 3
/**
* @brief Encode 5GMM Capability IE (9.11.3 of 3GPP TS 24.501)
*/
int encode_5gmm_capability(uint8_t *buffer, const FGMMCapability *fgmmcapability, uint32_t len)
{ {
int encoded = 0; int encoded = 0;
if (iei) {
// Ensure buffer length is sufficient
if (len < MIN_LENGTH_5GMM_CAPABILITY) {
printf("Buffer length insufficient for encoding 5GMM capability\n");
return -1;
}
if (fgmmcapability->iei) {
*buffer = fgmmcapability->iei; *buffer = fgmmcapability->iei;
encoded++; encoded++;
*(buffer + encoded) = fgmmcapability->length; } else {
printf("Missing IEI in fgmmcapability\n");
}
buffer[encoded] = fgmmcapability->length;
encoded++; encoded++;
*(buffer + encoded) = fgmmcapability->value;
// Encode octet 3 (mandatory)
buffer[encoded] = (fgmmcapability->sgc << 7) | (fgmmcapability->iphc_cp_cIoT << 6) | (fgmmcapability->n3_data << 5)
| (fgmmcapability->cp_cIoT << 4) | (fgmmcapability->restrict_ec << 3) | (fgmmcapability->lpp << 2)
| (fgmmcapability->ho_attach << 1) | fgmmcapability->s1_mode;
encoded++;
// Encode octet 4
if (fgmmcapability->length >= 2) {
buffer[encoded] = (fgmmcapability->racs << 7) | (fgmmcapability->nssaa << 6) | (fgmmcapability->lcs << 5)
| (fgmmcapability->v2x_cnpc5 << 4) | (fgmmcapability->v2x_cepc5 << 3) | (fgmmcapability->v2x << 2)
| (fgmmcapability->up_cIoT << 1) | fgmmcapability->srvcc;
encoded++; encoded++;
} }
// Encode octet 5
if (fgmmcapability->length >= 3) {
buffer[encoded] = (fgmmcapability->ehc_CP_ciot << 3) | (fgmmcapability->multiple_eUP << 2) | (fgmmcapability->wusa << 1)
| fgmmcapability->cag;
encoded++;
}
// Encode octets 6 - 15
if (fgmmcapability->length >= 4)
memset(&buffer[encoded], 0, fgmmcapability->length - 3);
return encoded; return encoded;
} }
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include "OctetString.h" #include "OctetString.h"
...@@ -39,9 +40,28 @@ ...@@ -39,9 +40,28 @@
typedef struct { typedef struct {
uint8_t iei; uint8_t iei;
uint8_t length; uint8_t length;
uint8_t value; bool sgc;
bool iphc_cp_cIoT;
bool n3_data;
bool cp_cIoT;
bool restrict_ec;
bool lpp;
bool ho_attach;
bool s1_mode;
bool racs;
bool nssaa;
bool lcs;
bool v2x_cnpc5;
bool v2x_cepc5;
bool v2x;
bool up_cIoT;
bool srvcc;
bool ehc_CP_ciot;
bool multiple_eUP;
bool wusa;
bool cag;
} FGMMCapability; } FGMMCapability;
int encode_5gmm_capability(const FGMMCapability *fgmmcapability, uint8_t iei, uint8_t *buffer, uint32_t len); int encode_5gmm_capability(uint8_t *buffer, const FGMMCapability *fgmmcapability, uint32_t len);
#endif /* FGMM_CAPABILITY_H_ */ #endif /* FGMM_CAPABILITY_H_ */
...@@ -30,11 +30,14 @@ ...@@ -30,11 +30,14 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include "TLVEncoder.h" #include "TLVEncoder.h"
#include "TLVDecoder.h" #include "TLVDecoder.h"
#include "FGSRegistrationType.h" #include "FGSRegistrationType.h"
#define NO_FOLLOW_ON_REQUEST 0x0
#define FOLLOW_ON_REQUEST 0x08
int decode_5gs_registration_type(FGSRegistrationType *fgsregistrationtype, uint8_t iei, uint8_t value, uint32_t len) int decode_5gs_registration_type(FGSRegistrationType *fgsregistrationtype, uint8_t iei, uint8_t value, uint32_t len)
{ {
int decoded = 0; int decoded = 0;
...@@ -49,14 +52,14 @@ int decode_5gs_registration_type(FGSRegistrationType *fgsregistrationtype, uint8 ...@@ -49,14 +52,14 @@ int decode_5gs_registration_type(FGSRegistrationType *fgsregistrationtype, uint8
return decoded; return decoded;
} }
int encode_5gs_registration_type(const FGSRegistrationType *fgsregistrationtype) /**
* @brief Encode 5GS registration type (9.11.3.7 of 3GPP TS 24.501)
* Note: mandatory IE (IEI = 0)
*/
int encode_5gs_registration_type(const FGSRegistrationType *fgsregistrationtype, bool follow_on_request)
{ {
uint8_t bufferReturn; // Follow-on request bit
uint8_t *buffer = &bufferReturn; uint8_t foR = follow_on_request ? FOLLOW_ON_REQUEST : NO_FOLLOW_ON_REQUEST;
uint8_t encoded = 0; // Return encoded value
uint8_t iei = 0; return (foR | (*fgsregistrationtype & 0x7));
*(buffer + encoded) = 0x00 | (iei & 0xf0) | 0x8 | (*fgsregistrationtype & 0x7);
encoded++;
return bufferReturn;
} }
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include "OctetString.h" #include "OctetString.h"
#ifndef FGS_REGISTRATION_TYPE_H_ #ifndef FGS_REGISTRATION_TYPE_H_
...@@ -41,13 +41,15 @@ ...@@ -41,13 +41,15 @@
typedef uint8_t FGSRegistrationType; typedef uint8_t FGSRegistrationType;
// clang-format off // clang-format off
/* 3GPP TS 24.501 Table 9.11.3.7.1: 5GS registration type IE */
#define INITIAL_REGISTRATION 0b001 #define INITIAL_REGISTRATION 0b001
#define MOBILITY_REGISTRATION_UPDATING 0b010 #define MOBILITY_REGISTRATION_UPDATING 0b010
#define PERIODIC_REGISTRATION_UPDATING 0b011 #define PERIODIC_REGISTRATION_UPDATING 0b011
#define EMERGENCY_REGISTRATION 0b100 #define EMERGENCY_REGISTRATION 0b100
#define REG_TYPE_RESERVED 0b111
// clang-format on // clang-format on
int encode_5gs_registration_type(const FGSRegistrationType *fgsregistrationtype); int encode_5gs_registration_type(const FGSRegistrationType *fgsregistrationtype, bool follow_on_request);
int decode_5gs_registration_type(FGSRegistrationType *fgsregistrationtype, uint8_t iei, uint8_t value, uint32_t len); int decode_5gs_registration_type(FGSRegistrationType *fgsregistrationtype, uint8_t iei, uint8_t value, uint32_t len);
......
...@@ -72,9 +72,10 @@ int encode_registration_request(const registration_request_msg *registration_req ...@@ -72,9 +72,10 @@ int encode_registration_request(const registration_request_msg *registration_req
{ {
int encoded = 0; int encoded = 0;
int encode_result = 0; int encode_result = 0;
bool is_for = true; // Follow-on request pending
*(buffer + encoded) = ((encode_nas_key_set_identifier(&registration_request->naskeysetidentifier, IEI_NULL) & 0x0f) << 4) *(buffer + encoded) = ((encode_nas_key_set_identifier(&registration_request->naskeysetidentifier, IEI_NULL) & 0x0f) << 4)
| (encode_5gs_registration_type(&registration_request->fgsregistrationtype) & 0x0f); | (encode_5gs_registration_type(&registration_request->fgsregistrationtype, is_for) & 0x0f);
encoded++; encoded++;
if ((encode_result = encode_5gs_mobile_identity(&registration_request->fgsmobileidentity, 0, buffer + encoded, len - encoded)) if ((encode_result = encode_5gs_mobile_identity(&registration_request->fgsmobileidentity, 0, buffer + encoded, len - encoded))
...@@ -86,12 +87,7 @@ int encode_registration_request(const registration_request_msg *registration_req ...@@ -86,12 +87,7 @@ int encode_registration_request(const registration_request_msg *registration_req
if ((registration_request->presencemask & REGISTRATION_REQUEST_5GMM_CAPABILITY_PRESENT) if ((registration_request->presencemask & REGISTRATION_REQUEST_5GMM_CAPABILITY_PRESENT)
== REGISTRATION_REQUEST_5GMM_CAPABILITY_PRESENT) { == REGISTRATION_REQUEST_5GMM_CAPABILITY_PRESENT) {
if ((encode_result = encode_5gmm_capability(&registration_request->fgmmcapability, if ((encode_result = encode_5gmm_capability(buffer + encoded, &registration_request->fgmmcapability, len - encoded)) < 0)
REGISTRATION_REQUEST_5GMM_CAPABILITY_IEI,
buffer + encoded,
len - encoded))
< 0)
// Return in case of error
return encode_result; return encode_result;
else else
encoded += encode_result; encoded += encode_result;
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "NasKeySetIdentifier.h" #include "NasKeySetIdentifier.h"
#include "NrUESecurityCapability.h" #include "NrUESecurityCapability.h"
#include "SecurityHeaderType.h" #include "SecurityHeaderType.h"
#include "FGCNasMessageContainer.h"
#ifndef REGISTRATION_REQUEST_H_ #ifndef REGISTRATION_REQUEST_H_
#define REGISTRATION_REQUEST_H_ #define REGISTRATION_REQUEST_H_
...@@ -102,6 +103,7 @@ typedef struct registration_request_msg_tag { ...@@ -102,6 +103,7 @@ typedef struct registration_request_msg_tag {
NasKeySetIdentifier naskeysetidentifier; NasKeySetIdentifier naskeysetidentifier;
FGSMobileIdentity fgsmobileidentity; FGSMobileIdentity fgsmobileidentity;
/* Optional fields */ /* Optional fields */
FGCNasMessageContainer fgsnasmessagecontainer;
uint32_t presencemask; uint32_t presencemask;
FGMMCapability fgmmcapability; FGMMCapability fgmmcapability;
NrUESecurityCapability nruesecuritycapability; NrUESecurityCapability nruesecuritycapability;
......
...@@ -199,6 +199,24 @@ static int fill_suci(FGSMobileIdentity *mi, const uicc_t *uicc) ...@@ -199,6 +199,24 @@ static int fill_suci(FGSMobileIdentity *mi, const uicc_t *uicc)
mi->suci.mccdigit2 = uicc->imsiStr[1] - '0'; mi->suci.mccdigit2 = uicc->imsiStr[1] - '0';
mi->suci.mccdigit3 = uicc->imsiStr[2] - '0'; mi->suci.mccdigit3 = uicc->imsiStr[2] - '0';
memcpy(mi->suci.schemeoutput, uicc->imsiStr + 3 + uicc->nmc_size, strlen(uicc->imsiStr) - (3 + uicc->nmc_size)); memcpy(mi->suci.schemeoutput, uicc->imsiStr + 3 + uicc->nmc_size, strlen(uicc->imsiStr) - (3 + uicc->nmc_size));
LOG_D(NAS,
"SUCI in registration request: SUPI type: %d Type of Identity: %u MCC: %u%u%u, MNC: %u%u%u, \
Routing Indicator %d%d%d%d Protection Scheme ID: %u, Home Network PKI: %u, Scheme Output: %s\n",
mi->suci.supiformat,
mi->suci.typeofidentity,
mi->suci.mccdigit1,
mi->suci.mccdigit2,
mi->suci.mccdigit3,
mi->suci.mncdigit1,
mi->suci.mncdigit2,
uicc->nmc_size == 2 ? 0 : mi->suci.mncdigit3,
mi->suci.routingindicatordigit1,
mi->suci.routingindicatordigit2,
mi->suci.routingindicatordigit3,
mi->suci.routingindicatordigit4,
mi->suci.protectionschemeId,
mi->suci.homenetworkpki,
mi->suci.schemeoutput);
return sizeof(Suci5GSMobileIdentity_t); return sizeof(Suci5GSMobileIdentity_t);
} }
...@@ -206,6 +224,18 @@ static int fill_guti(FGSMobileIdentity *mi, const Guti5GSMobileIdentity_t *guti) ...@@ -206,6 +224,18 @@ static int fill_guti(FGSMobileIdentity *mi, const Guti5GSMobileIdentity_t *guti)
{ {
AssertFatal(guti != NULL, "UE has no GUTI\n"); AssertFatal(guti != NULL, "UE has no GUTI\n");
mi->guti = *guti; mi->guti = *guti;
LOG_D(NAS,
"5G-GUTI in registration request: MCC: %u%u%u, MNC: %u%u, AMF Region ID: %u, AMF Set ID: %u, AMF Pointer: %u, 5G-TMSI: "
"%u\n",
mi->guti.mccdigit1,
mi->guti.mccdigit2,
mi->guti.mccdigit3,
mi->guti.mncdigit1,
mi->guti.mncdigit2,
mi->guti.amfregionid,
mi->guti.amfsetid,
mi->guti.amfpointer,
mi->guti.tmsi);
return 13; return 13;
} }
...@@ -458,53 +488,210 @@ nr_ue_nas_t *get_ue_nas_info(module_id_t module_id) ...@@ -458,53 +488,210 @@ nr_ue_nas_t *get_ue_nas_info(module_id_t module_id)
return &nr_ue_nas[module_id]; return &nr_ue_nas[module_id];
} }
void generateRegistrationRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) static FGSRegistrationType set_fgs_ksi(nr_ue_nas_t *nas)
{ {
int size = sizeof(fgmm_msg_header_t); if (nas->fiveGMM_mode == FGS_IDLE) {
fgmm_nas_message_plain_t plain = {0}; /**
* the UE is IDLE, therefore ngKSI was deleted, along all K_AMF, ciphering key, integrity key
* (i.e. the 5G NAS security context associated with the ngKSI is no longer valid)
* see 4.4.2 of 3GPP TS 24.501
*/
return NAS_KEY_SET_IDENTIFIER_NOT_AVAILABLE;
}
return 0x0;
}
// Plain 5GMM header /**
plain.header = set_mm_header(FGS_REGISTRATION_REQUEST, PLAIN_5GS_MSG); * @brief Set contents of 5GMM capability
size += sizeof(plain.header); * @note Currently hardcoded, sending min length only (1 octet)
*/
static FGMMCapability set_fgmm_capability(nr_ue_nas_t *nas)
{
FGMMCapability cap = {0};
cap.iei = REGISTRATION_REQUEST_5GMM_CAPABILITY_IEI;
cap.length = 1;
cap.sgc = 0;
cap.iphc_cp_cIoT = 0;
cap.n3_data = 0;
cap.cp_cIoT = 0;
cap.restrict_ec = 0;
cap.lpp = 1;
cap.ho_attach = 1;
cap.s1_mode = 1;
if (cap.length == 1)
return cap; // Send minimum length only, 1 octet
cap.racs = 0;
cap.nssaa = 0;
cap.lcs = 0;
cap.v2x_cnpc5 = 0;
cap.v2x_cepc5 = 0;
cap.v2x = 0;
cap.up_cIoT = 0;
cap.srvcc = 0;
cap.ehc_CP_ciot = 0;
cap.multiple_eUP = 0;
cap.wusa = 0;
cap.cag = 0;
return cap;
}
// Plain 5GMM msg static FGSRegistrationType set_fgs_registration_type(nr_ue_nas_t *nas)
registration_request_msg *mm_msg = &plain.mm_msg.registration_request; {
mm_msg->fgsregistrationtype = INITIAL_REGISTRATION; if (nas->fiveGMM_state == FGS_REGISTERED && nas->fiveGMM_mode == FGS_IDLE && nas->t3512) {
mm_msg->naskeysetidentifier.naskeysetidentifier = 1; // TODO: if the timer expires, do PERIODIC_REGISTRATION_UPDATING
/** The UE shall initiate the registration procedure for
* mobility and periodic registration update according to
* 5.5.1.3.2 of 3GPP TS 24.501: Mobility and periodic
* registration update initiation */
LOG_E(NAS, "Registration type periodic registration updating is not handled\n");
return REG_TYPE_RESERVED;
} else if (nas->fiveGMM_state == FGS_REGISTERED) {
// in any other case, The UE in state 5GMM-REGISTERED shall indicate "mobility registration updating".
return MOBILITY_REGISTRATION_UPDATING;
}
if (nas->fiveGMM_mode == FGS_CONNECTED && nas->is_rrc_inactive) {
/** the UE shall do the registration procedure for mobility
* and/or periodic registration update depending on the
* indication received from the lower layers according to
* 5.3.1.4 of 3GPP TS 24.501: 5GMM-CONNECTED mode with RRC inactive indication */
LOG_E(NAS, "RRC inactive indication not handled by NAS\n");
return REG_TYPE_RESERVED;
}
return INITIAL_REGISTRATION;
}
/**
* @brief Generate 5GS Registration Request (8.2.6 of 3GPP TS 24.501)
*/
void generateRegistrationRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas, bool is_security_mode)
{
LOG_I(NAS, "Generate Initial NAS Message: Registration Request\n");
int size = sizeof(fgmm_msg_header_t); // cleartext size
fgmm_nas_msg_security_protected_t sp = {0};
/** Check whether the UE has a valid current 5G NAS security context
and set security protected 5GS NAS message header (see 9.1.1 of 3GPP TS 24.501) */
bool has_security_context = nas->security_container && nas->security_container->integrity_context;
if (has_security_context) {
sp.header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE;
sp.header.security_header_type = INTEGRITY_PROTECTED;
sp.header.sequence_number = nas->security.nas_count_ul & 0xff;
size += 7;
}
// Plain 5GMM message
sp.plain.header = set_mm_header(FGS_REGISTRATION_REQUEST, PLAIN_5GS_MSG);
size += sizeof(sp.plain.header);
registration_request_msg *rr = &sp.plain.mm_msg.registration_request;
// 5GMM Registration Type
rr->fgsregistrationtype = set_fgs_registration_type(nas);
size += 1; size += 1;
if (nas->guti) { if (rr->fgsregistrationtype == REG_TYPE_RESERVED) {
size += fill_guti(&mm_msg->fgsmobileidentity, nas->guti); // currently only REG_TYPE_RESERVED is supported
LOG_E(NAS, "Initial NAS Message: Registration Request failed\n");
return;
}
// NAS Key Set Identifier
rr->naskeysetidentifier.tsc = NAS_KEY_SET_IDENTIFIER_NATIVE;
rr->naskeysetidentifier.naskeysetidentifier = set_fgs_ksi(nas);
size += 1;
// 5GMM Mobile Identity
if(nas->guti){
size += fill_guti(&rr->fgsmobileidentity, nas->guti);
} else { } else {
size += fill_suci(&mm_msg->fgsmobileidentity, nas->uicc); size += fill_suci(&rr->fgsmobileidentity, nas->uicc);
} }
#if 0 // Security Capability
/* This cannot be sent in clear, the core network Open5GS rejects the UE. rr->presencemask |= REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_PRESENT;
* TODO: do we have to send this at some point? rr->nruesecuritycapability.iei = REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_IEI;
* For the time being, let's keep it here for later proper fix. rr->nruesecuritycapability.length = 8;
*/ rr->nruesecuritycapability.fg_EA = 0xe0;
mm_msg->registration_request.presencemask |= REGISTRATION_REQUEST_5GMM_CAPABILITY_PRESENT; rr->nruesecuritycapability.fg_IA = 0x60;
mm_msg->registration_request.fgmmcapability.iei = REGISTRATION_REQUEST_5GMM_CAPABILITY_IEI; rr->nruesecuritycapability.EEA = 0;
mm_msg->registration_request.fgmmcapability.length = 1; rr->nruesecuritycapability.EIA = 0;
mm_msg->registration_request.fgmmcapability.value = 0x7;
size += 3;
#endif
mm_msg->presencemask |= REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_PRESENT;
mm_msg->nruesecuritycapability.iei = REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_IEI;
mm_msg->nruesecuritycapability.length = 8;
mm_msg->nruesecuritycapability.fg_EA = 0xe0;
mm_msg->nruesecuritycapability.fg_IA = 0x60;
mm_msg->nruesecuritycapability.EEA = 0;
mm_msg->nruesecuritycapability.EIA = 0;
size += 10; size += 10;
// encode the message /* Create a copy of the cleartext 5GMM message, add non-cleartext IEs if necessary */
initialNasMsg->nas_data = malloc16_clear(size * sizeof(*initialNasMsg->nas_data)); fgmm_nas_message_plain_t full_mm = sp.plain;
nas->registration_request_buf = initialNasMsg->nas_data; registration_request_msg *full_rr = &full_mm.mm_msg.registration_request;
int size_nct = size; // non-cleartext size
bool cleartext_only = true;
/* 5GMM Capability (non-cleartext IE) - 24.501 8.2.6.3
The UE shall include this IE, unless the UE performs a periodic registration updating procedure. */
if (full_rr->fgsregistrationtype != PERIODIC_REGISTRATION_UPDATING) {
cleartext_only = false; // The UE needs to send non-cleartext IE
full_rr->presencemask |= REGISTRATION_REQUEST_5GMM_CAPABILITY_PRESENT;
full_rr->fgmmcapability = set_fgmm_capability(nas);
FGMMCapability *cap = &full_rr->fgmmcapability;
size_nct += sizeof(cap->length) + sizeof(cap->iei) + cap->length;
}
initialNasMsg->length = mm_msg_encode(&plain, initialNasMsg->nas_data, size); if (is_security_mode) {
nas->registration_request_len = initialNasMsg->length; /* Encode both cleartext IEs and non-cleartext IEs Registration Request message in Security Mode Complete.
The UE includes the full Registration Request in the NAS container IE
and sends it within the Security Mode Complete message. (24.501 4.4.6, 23.502 4.2.2.2.2) */
LOG_D(NAS, "Full Initial NAS Message: Registration Request in the NAS container of Security Mode Complete\n");
initialNasMsg->nas_data = malloc_or_fail(size_nct * sizeof(*initialNasMsg->nas_data));
initialNasMsg->length = mm_msg_encode(&full_mm, initialNasMsg->nas_data, size_nct);
} else if (!has_security_context) {
/* If no valid 5G NAS security context exists, the UE sends a plain Registration Request including cleartext IEs only. */
LOG_D(NAS, "Plain Initial NAS Message: Registration Request\n");
initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data));
initialNasMsg->length = mm_msg_encode(&sp.plain, initialNasMsg->nas_data, size);
} else {
/* If the UE has a valid current 5G NAS security context, then it includes the entire 5GMM NAS Registration Request
(with both cleartext and non-cleartext IEs) in the NAS message container IE. The value of the NAS message container IE is
then ciphered. The UE sends a 5GMM NAS Registration Request message containing cleartext IEs along with the NAS message
container IE. */
LOG_D(NAS, "Initial NAS Message: Registration Request with ciphered NAS container\n");
// NAS message container
if (!cleartext_only) {
OctetString *nasmessagecontainercontents = &rr->fgsnasmessagecontainer.nasmessagecontainercontents;
nasmessagecontainercontents->value = calloc_or_fail(size_nct, sizeof(*nasmessagecontainercontents->value));
nasmessagecontainercontents->length = mm_msg_encode(&full_mm, nasmessagecontainercontents->value, size_nct);
size += (nasmessagecontainercontents->length + 2);
rr->presencemask |= REGISTRATION_REQUEST_NAS_MESSAGE_CONTAINER_PRESENT;
}
// Allocate buffer (including NAS message container size)
initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data));
// Security protected header encoding
int security_header_len = nas_protected_security_header_encode(initialNasMsg->nas_data, &sp.header, size);
initialNasMsg->length =
security_header_len
+ mm_msg_encode(&sp.plain, initialNasMsg->nas_data + security_header_len, size - security_header_len);
/* integrity protection */
nas_stream_cipher_t stream_cipher;
AssertFatal(nas->security.nas_count_ul <= 0xffffff, "fatal: NAS COUNT UL too big (todo: fix that)\n");
uint8_t mac[4];
stream_cipher.context = nas->security_container->integrity_context;
stream_cipher.count = nas->security.nas_count_ul++;
stream_cipher.bearer = 1;
stream_cipher.direction = 0;
// Security protected header is cleartext except the SN field
uint8_t cleartext_len = sizeof(sp.header) - 1;
// Message to be integrity protected
stream_cipher.message = initialNasMsg->nas_data + cleartext_len;
// Length of integrity protected message in bits
stream_cipher.blength = (initialNasMsg->length - cleartext_len) << 3;
stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac);
uint8_t mac_len = sizeof(sp.header.message_authentication_code);
uint8_t mac_start_octet = 2;
LOG_D(NAS, "Integrity protected initial NAS message: mac = %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]);
for (int i = 0; i < mac_len; i++) {
initialNasMsg->nas_data[mac_start_octet + i] = mac[i];
}
}
} }
void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas)
...@@ -530,6 +717,8 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) ...@@ -530,6 +717,8 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas)
// 5G-S-TMSI // 5G-S-TMSI
size += fill_fgstmsi(&mm_msg->fiveg_s_tmsi, nas->guti); size += fill_fgstmsi(&mm_msg->fiveg_s_tmsi, nas->guti);
/* message encoding */
initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data));
if (security_protected) { if (security_protected) {
fgmm_nas_msg_security_protected_t sp = {0}; fgmm_nas_msg_security_protected_t sp = {0};
...@@ -542,9 +731,6 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) ...@@ -542,9 +731,6 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas)
// Payload: plain message // Payload: plain message
sp.plain = plain; sp.plain = plain;
// Allocate NAS message for encoding
initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data));
// security protected encoding // security protected encoding
int security_header_len = nas_protected_security_header_encode(initialNasMsg->nas_data, &sp.header, size); int security_header_len = nas_protected_security_header_encode(initialNasMsg->nas_data, &sp.header, size);
initialNasMsg->length = initialNasMsg->length =
...@@ -578,8 +764,6 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) ...@@ -578,8 +764,6 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas)
initialNasMsg->nas_data[2 + i] = mac[i]; initialNasMsg->nas_data[2 + i] = mac[i];
} else { } else {
// plain encoding // plain encoding
// Allocate NAS message for encoding
initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data));
initialNasMsg->length = mm_msg_encode(&plain, initialNasMsg->nas_data, size); initialNasMsg->length = mm_msg_encode(&plain, initialNasMsg->nas_data, size);
LOG_I(NAS, "PLAIN_5GS_MSG initial NAS message: Service Request with length %d \n", initialNasMsg->length); LOG_I(NAS, "PLAIN_5GS_MSG initial NAS message: Service Request with length %d \n", initialNasMsg->length);
} }
...@@ -665,9 +849,14 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia ...@@ -665,9 +849,14 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia
fgs_security_mode_complete_msg *mm_msg = &plain->mm_msg.fgs_security_mode_complete; fgs_security_mode_complete_msg *mm_msg = &plain->mm_msg.fgs_security_mode_complete;
size += fill_imeisv(&mm_msg->fgsmobileidentity, nas->uicc); size += fill_imeisv(&mm_msg->fgsmobileidentity, nas->uicc);
mm_msg->fgsnasmessagecontainer.nasmessagecontainercontents.value = nas->registration_request_buf; /* After activating a 5G NAS security context resulting from a security mode control send the full
mm_msg->fgsnasmessagecontainer.nasmessagecontainercontents.length = nas->registration_request_len; NAS Registration Request in the message container IE of the SECURITY MODE COMPLETE message (24.501 4.4.6) */
size += (nas->registration_request_len + 2); as_nas_info_t rr;
generateRegistrationRequest(&rr, nas, true);
FGCNasMessageContainer *container = &mm_msg->fgsnasmessagecontainer;
container->nasmessagecontainercontents.value = rr.nas_data;
container->nasmessagecontainercontents.length = rr.length;
size += (rr.length + 2);
// encode the message // encode the message
initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data)); initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data));
...@@ -812,6 +1001,8 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, ...@@ -812,6 +1001,8 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas,
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
initialNasMsg->nas_data[2 + i] = mac[i]; initialNasMsg->nas_data[2 + i] = mac[i];
} }
/* Set NAS 5GMM state */
nas->fiveGMM_state = FGS_REGISTERED;
} }
/** /**
...@@ -1010,6 +1201,8 @@ static void generateDeregistrationRequest(nr_ue_nas_t *nas, as_nas_info_t *initi ...@@ -1010,6 +1201,8 @@ static void generateDeregistrationRequest(nr_ue_nas_t *nas, as_nas_info_t *initi
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
initialNasMsg->nas_data[2 + i] = mac[i]; initialNasMsg->nas_data[2 + i] = mac[i];
} }
/* Set NAS 5GMM state */
nas->fiveGMM_state = FGS_DEREGISTERED_INITIATED;
} }
static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, nas_pdu_session_req_t *pdu_req) static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, nas_pdu_session_req_t *pdu_req)
...@@ -1438,6 +1631,8 @@ void *nas_nrue(void *args_p) ...@@ -1438,6 +1631,8 @@ void *nas_nrue(void *args_p)
break; break;
case FGS_DEREGISTRATION_ACCEPT_UE_ORIGINATING: case FGS_DEREGISTRATION_ACCEPT_UE_ORIGINATING:
LOG_I(NAS, "received deregistration accept\n"); LOG_I(NAS, "received deregistration accept\n");
/* Set NAS 5GMM state */
nas->fiveGMM_state = FGS_DEREGISTERED;
break; break;
case FGS_PDU_SESSION_ESTABLISHMENT_ACC: case FGS_PDU_SESSION_ESTABLISHMENT_ACC:
handle_pdu_session_accept(pdu_buffer, pdu_length); handle_pdu_session_accept(pdu_buffer, pdu_length);
......
...@@ -51,6 +51,24 @@ ...@@ -51,6 +51,24 @@
/* 3GPP TS 24.501: 9.11.3.50 Service type */ /* 3GPP TS 24.501: 9.11.3.50 Service type */
#define SERVICE_TYPE_DATA 0x1 #define SERVICE_TYPE_DATA 0x1
typedef enum fgs_mm_state_e {
FGS_DEREGISTERED,
FGS_DEREGISTERED_INITIATED,
FGS_REGISTERED_INITIATED,
FGS_REGISTERED,
FGS_SERVICE_REQUEST_INITIATED,
} fgs_mm_state_t;
/*
* 5GS mobility management (5GMM) modes
* 5.1.3.2.1.1 of TS 24.501
*/
typedef enum fgs_mm_mode_e {
FGS_NOT_CONNECTED,
FGS_IDLE,
FGS_CONNECTED,
} fgs_mm_mode_t;
/* Security Key for SA UE */ /* Security Key for SA UE */
typedef struct { typedef struct {
uint8_t kausf[32]; uint8_t kausf[32];
...@@ -66,18 +84,24 @@ typedef struct { ...@@ -66,18 +84,24 @@ typedef struct {
} ue_sa_security_key_t; } ue_sa_security_key_t;
typedef struct { typedef struct {
/* 5GS Mobility Management States (5.1.3.2.1 of 3GPP TS 24.501) */
fgs_mm_state_t fiveGMM_state;
/* 5GS Mobility Management mode */
fgs_mm_mode_t fiveGMM_mode;
uicc_t *uicc; uicc_t *uicc;
ue_sa_security_key_t security; ue_sa_security_key_t security;
stream_security_container_t *security_container; stream_security_container_t *security_container;
Guti5GSMobileIdentity_t *guti; Guti5GSMobileIdentity_t *guti;
bool termination_procedure; bool termination_procedure;
uint8_t *registration_request_buf;
uint32_t registration_request_len;
instance_t UE_id; instance_t UE_id;
/* RRC Inactive Indication */
bool is_rrc_inactive;
/* Timer T3512 */
int t3512;
} nr_ue_nas_t; } nr_ue_nas_t;
nr_ue_nas_t *get_ue_nas_info(module_id_t module_id); nr_ue_nas_t *get_ue_nas_info(module_id_t module_id);
void generateRegistrationRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas); void generateRegistrationRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas, bool is_security_mode);
void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas); void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas);
void *nas_nrue_task(void *args_p); void *nas_nrue_task(void *args_p);
void *nas_nrue(void *args_p); void *nas_nrue(void *args_p);
......
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