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
This commit is contained in:
@@ -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 @@
|
|||||||
#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 {
|
||||||
encoded++;
|
printf("Missing IEI in fgmmcapability\n");
|
||||||
*(buffer + encoded) = fgmmcapability->value;
|
}
|
||||||
|
|
||||||
|
buffer[encoded] = fgmmcapability->length;
|
||||||
|
encoded++;
|
||||||
|
|
||||||
|
// 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 @@
|
|||||||
#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 @@
|
|||||||
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 @@
|
|||||||
#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
|
|||||||
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 @@
|
|||||||
#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 @@
|
|||||||
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
|
|||||||
{
|
{
|
||||||
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(®istration_request->naskeysetidentifier, IEI_NULL) & 0x0f) << 4)
|
*(buffer + encoded) = ((encode_nas_key_set_identifier(®istration_request->naskeysetidentifier, IEI_NULL) & 0x0f) << 4)
|
||||||
| (encode_5gs_registration_type(®istration_request->fgsregistrationtype) & 0x0f);
|
| (encode_5gs_registration_type(®istration_request->fgsregistrationtype, is_for) & 0x0f);
|
||||||
encoded++;
|
encoded++;
|
||||||
|
|
||||||
if ((encode_result = encode_5gs_mobile_identity(®istration_request->fgsmobileidentity, 0, buffer + encoded, len - encoded))
|
if ((encode_result = encode_5gs_mobile_identity(®istration_request->fgsmobileidentity, 0, buffer + encoded, len - encoded))
|
||||||
@@ -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(®istration_request->fgmmcapability,
|
if ((encode_result = encode_5gmm_capability(buffer + encoded, ®istration_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 @@
|
|||||||
#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 {
|
|||||||
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;
|
||||||
|
|||||||
+241
-46
@@ -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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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;
|
||||||
|
|
||||||
// Plain 5GMM msg
|
cap.sgc = 0;
|
||||||
registration_request_msg *mm_msg = &plain.mm_msg.registration_request;
|
cap.iphc_cp_cIoT = 0;
|
||||||
mm_msg->fgsregistrationtype = INITIAL_REGISTRATION;
|
cap.n3_data = 0;
|
||||||
mm_msg->naskeysetidentifier.naskeysetidentifier = 1;
|
cap.cp_cIoT = 0;
|
||||||
size += 1;
|
cap.restrict_ec = 0;
|
||||||
if (nas->guti) {
|
cap.lpp = 1;
|
||||||
size += fill_guti(&mm_msg->fgsmobileidentity, nas->guti);
|
cap.ho_attach = 1;
|
||||||
} else {
|
cap.s1_mode = 1;
|
||||||
size += fill_suci(&mm_msg->fgsmobileidentity, nas->uicc);
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FGSRegistrationType set_fgs_registration_type(nr_ue_nas_t *nas)
|
||||||
|
{
|
||||||
|
if (nas->fiveGMM_state == FGS_REGISTERED && nas->fiveGMM_mode == FGS_IDLE && nas->t3512) {
|
||||||
|
// 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 0
|
if (nas->fiveGMM_mode == FGS_CONNECTED && nas->is_rrc_inactive) {
|
||||||
/* This cannot be sent in clear, the core network Open5GS rejects the UE.
|
/** the UE shall do the registration procedure for mobility
|
||||||
* TODO: do we have to send this at some point?
|
* and/or periodic registration update depending on the
|
||||||
* For the time being, let's keep it here for later proper fix.
|
* indication received from the lower layers according to
|
||||||
*/
|
* 5.3.1.4 of 3GPP TS 24.501: 5GMM-CONNECTED mode with RRC inactive indication */
|
||||||
mm_msg->registration_request.presencemask |= REGISTRATION_REQUEST_5GMM_CAPABILITY_PRESENT;
|
LOG_E(NAS, "RRC inactive indication not handled by NAS\n");
|
||||||
mm_msg->registration_request.fgmmcapability.iei = REGISTRATION_REQUEST_5GMM_CAPABILITY_IEI;
|
return REG_TYPE_RESERVED;
|
||||||
mm_msg->registration_request.fgmmcapability.length = 1;
|
}
|
||||||
mm_msg->registration_request.fgmmcapability.value = 0x7;
|
|
||||||
size += 3;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mm_msg->presencemask |= REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_PRESENT;
|
return INITIAL_REGISTRATION;
|
||||||
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;
|
* @brief Generate 5GS Registration Request (8.2.6 of 3GPP TS 24.501)
|
||||||
mm_msg->nruesecuritycapability.EEA = 0;
|
*/
|
||||||
mm_msg->nruesecuritycapability.EIA = 0;
|
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;
|
||||||
|
if (rr->fgsregistrationtype == REG_TYPE_RESERVED) {
|
||||||
|
// 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 {
|
||||||
|
size += fill_suci(&rr->fgsmobileidentity, nas->uicc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Security Capability
|
||||||
|
rr->presencemask |= REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_PRESENT;
|
||||||
|
rr->nruesecuritycapability.iei = REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_IEI;
|
||||||
|
rr->nruesecuritycapability.length = 8;
|
||||||
|
rr->nruesecuritycapability.fg_EA = 0xe0;
|
||||||
|
rr->nruesecuritycapability.fg_IA = 0x60;
|
||||||
|
rr->nruesecuritycapability.EEA = 0;
|
||||||
|
rr->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)
|
|||||||
// 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)
|
|||||||
// 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)
|
|||||||
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
|
|||||||
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,
|
|||||||
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
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
/* 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 {
|
|||||||
} 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);
|
||||||
|
|||||||
Reference in New Issue
Block a user