diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 26b9f51046..3fbe3663f0 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -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())) { as_nas_info_t initialNasMsg; 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_length = initialNasMsg.length; } else { diff --git a/openair3/NAS/NR_UE/5GS/5GMM/IES/FGMMCapability.c b/openair3/NAS/NR_UE/5GS/5GMM/IES/FGMMCapability.c index c053070518..8e5c7d1364 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/IES/FGMMCapability.c +++ b/openair3/NAS/NR_UE/5GS/5GMM/IES/FGMMCapability.c @@ -35,16 +35,55 @@ #include "TLVDecoder.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; - 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; encoded++; - *(buffer + encoded) = fgmmcapability->length; - encoded++; - *(buffer + encoded) = fgmmcapability->value; + } else { + printf("Missing IEI in fgmmcapability\n"); + } + + 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++; } + + // 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; } diff --git a/openair3/NAS/NR_UE/5GS/5GMM/IES/FGMMCapability.h b/openair3/NAS/NR_UE/5GS/5GMM/IES/FGMMCapability.h index b125935f9d..fcbdded058 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/IES/FGMMCapability.h +++ b/openair3/NAS/NR_UE/5GS/5GMM/IES/FGMMCapability.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "OctetString.h" @@ -39,9 +40,28 @@ typedef struct { uint8_t iei; 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; -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_ */ diff --git a/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSRegistrationType.c b/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSRegistrationType.c index 81825f585a..68861c1142 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSRegistrationType.c +++ b/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSRegistrationType.c @@ -30,11 +30,14 @@ #include #include #include - +#include #include "TLVEncoder.h" #include "TLVDecoder.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 decoded = 0; @@ -49,14 +52,14 @@ int decode_5gs_registration_type(FGSRegistrationType *fgsregistrationtype, uint8 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; - uint8_t *buffer = &bufferReturn; - uint8_t encoded = 0; - uint8_t iei = 0; - *(buffer + encoded) = 0x00 | (iei & 0xf0) | 0x8 | (*fgsregistrationtype & 0x7); - encoded++; - - return bufferReturn; + // Follow-on request bit + uint8_t foR = follow_on_request ? FOLLOW_ON_REQUEST : NO_FOLLOW_ON_REQUEST; + // Return encoded value + return (foR | (*fgsregistrationtype & 0x7)); } diff --git a/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSRegistrationType.h b/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSRegistrationType.h index ea42ac160e..7a0e832942 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSRegistrationType.h +++ b/openair3/NAS/NR_UE/5GS/5GMM/IES/FGSRegistrationType.h @@ -29,7 +29,7 @@ #include #include #include - +#include #include "OctetString.h" #ifndef FGS_REGISTRATION_TYPE_H_ @@ -41,13 +41,15 @@ typedef uint8_t FGSRegistrationType; // clang-format off +/* 3GPP TS 24.501 Table 9.11.3.7.1: 5GS registration type IE */ #define INITIAL_REGISTRATION 0b001 #define MOBILITY_REGISTRATION_UPDATING 0b010 #define PERIODIC_REGISTRATION_UPDATING 0b011 #define EMERGENCY_REGISTRATION 0b100 +#define REG_TYPE_RESERVED 0b111 // 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); diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/RegistrationRequest.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/RegistrationRequest.c index 8f6fea900e..6b124a3b81 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/RegistrationRequest.c +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/RegistrationRequest.c @@ -72,9 +72,10 @@ int encode_registration_request(const registration_request_msg *registration_req { int encoded = 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) - | (encode_5gs_registration_type(®istration_request->fgsregistrationtype) & 0x0f); + | (encode_5gs_registration_type(®istration_request->fgsregistrationtype, is_for) & 0x0f); 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) == REGISTRATION_REQUEST_5GMM_CAPABILITY_PRESENT) { - if ((encode_result = encode_5gmm_capability(®istration_request->fgmmcapability, - REGISTRATION_REQUEST_5GMM_CAPABILITY_IEI, - buffer + encoded, - len - encoded)) - < 0) - // Return in case of error + if ((encode_result = encode_5gmm_capability(buffer + encoded, ®istration_request->fgmmcapability, len - encoded)) < 0) return encode_result; else encoded += encode_result; diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/RegistrationRequest.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/RegistrationRequest.h index 686b0bc148..f820f3e03a 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/RegistrationRequest.h +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/RegistrationRequest.h @@ -35,6 +35,7 @@ #include "NasKeySetIdentifier.h" #include "NrUESecurityCapability.h" #include "SecurityHeaderType.h" +#include "FGCNasMessageContainer.h" #ifndef REGISTRATION_REQUEST_H_ #define REGISTRATION_REQUEST_H_ @@ -102,6 +103,7 @@ typedef struct registration_request_msg_tag { NasKeySetIdentifier naskeysetidentifier; FGSMobileIdentity fgsmobileidentity; /* Optional fields */ + FGCNasMessageContainer fgsnasmessagecontainer; uint32_t presencemask; FGMMCapability fgmmcapability; NrUESecurityCapability nruesecuritycapability; diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index d2764ac2e1..3c8da56280 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -199,6 +199,24 @@ static int fill_suci(FGSMobileIdentity *mi, const uicc_t *uicc) mi->suci.mccdigit2 = uicc->imsiStr[1] - '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)); + 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); } @@ -206,6 +224,18 @@ static int fill_guti(FGSMobileIdentity *mi, const Guti5GSMobileIdentity_t *guti) { AssertFatal(guti != NULL, "UE has no GUTI\n"); 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; } @@ -458,53 +488,210 @@ nr_ue_nas_t *get_ue_nas_info(module_id_t 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); - fgmm_nas_message_plain_t plain = {0}; + if (nas->fiveGMM_mode == FGS_IDLE) { + /** + * 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); - size += sizeof(plain.header); +/** + * @brief Set contents of 5GMM capability + * @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 - registration_request_msg *mm_msg = &plain.mm_msg.registration_request; - mm_msg->fgsregistrationtype = INITIAL_REGISTRATION; - mm_msg->naskeysetidentifier.naskeysetidentifier = 1; - size += 1; - if (nas->guti) { - size += fill_guti(&mm_msg->fgsmobileidentity, nas->guti); - } else { - size += fill_suci(&mm_msg->fgsmobileidentity, nas->uicc); + 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; +} + +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 - /* This cannot be sent in clear, the core network Open5GS rejects the UE. - * TODO: do we have to send this at some point? - * For the time being, let's keep it here for later proper fix. - */ - mm_msg->registration_request.presencemask |= REGISTRATION_REQUEST_5GMM_CAPABILITY_PRESENT; - mm_msg->registration_request.fgmmcapability.iei = REGISTRATION_REQUEST_5GMM_CAPABILITY_IEI; - mm_msg->registration_request.fgmmcapability.length = 1; - mm_msg->registration_request.fgmmcapability.value = 0x7; - size += 3; -#endif + 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; + } - 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; + 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; + 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; - // encode the message - initialNasMsg->nas_data = malloc16_clear(size * sizeof(*initialNasMsg->nas_data)); - nas->registration_request_buf = initialNasMsg->nas_data; + /* Create a copy of the cleartext 5GMM message, add non-cleartext IEs if necessary */ + fgmm_nas_message_plain_t full_mm = sp.plain; + 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); - nas->registration_request_len = initialNasMsg->length; + if (is_security_mode) { + /* 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) @@ -530,6 +717,8 @@ void generateServiceRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) // 5G-S-TMSI 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) { 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 sp.plain = plain; - // Allocate NAS message for encoding - initialNasMsg->nas_data = malloc_or_fail(size * sizeof(*initialNasMsg->nas_data)); - // security protected encoding int security_header_len = nas_protected_security_header_encode(initialNasMsg->nas_data, &sp.header, size); 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]; } else { // 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); 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; size += fill_imeisv(&mm_msg->fgsmobileidentity, nas->uicc); - mm_msg->fgsnasmessagecontainer.nasmessagecontainercontents.value = nas->registration_request_buf; - mm_msg->fgsnasmessagecontainer.nasmessagecontainercontents.length = nas->registration_request_len; - size += (nas->registration_request_len + 2); + /* After activating a 5G NAS security context resulting from a security mode control send the full + NAS Registration Request in the message container IE of the SECURITY MODE COMPLETE message (24.501 4.4.6) */ + 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 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++) { 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++) { 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) @@ -1438,6 +1631,8 @@ void *nas_nrue(void *args_p) break; case FGS_DEREGISTRATION_ACCEPT_UE_ORIGINATING: LOG_I(NAS, "received deregistration accept\n"); + /* Set NAS 5GMM state */ + nas->fiveGMM_state = FGS_DEREGISTERED; break; case FGS_PDU_SESSION_ESTABLISHMENT_ACC: handle_pdu_session_accept(pdu_buffer, pdu_length); diff --git a/openair3/NAS/NR_UE/nr_nas_msg.h b/openair3/NAS/NR_UE/nr_nas_msg.h index b4a97a36d7..c46ba6fcd4 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.h +++ b/openair3/NAS/NR_UE/nr_nas_msg.h @@ -51,6 +51,24 @@ /* 3GPP TS 24.501: 9.11.3.50 Service type */ #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 */ typedef struct { uint8_t kausf[32]; @@ -66,18 +84,24 @@ typedef struct { } ue_sa_security_key_t; 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; ue_sa_security_key_t security; stream_security_container_t *security_container; Guti5GSMobileIdentity_t *guti; bool termination_procedure; - uint8_t *registration_request_buf; - uint32_t registration_request_len; instance_t UE_id; + /* RRC Inactive Indication */ + bool is_rrc_inactive; + /* Timer T3512 */ + int t3512; } nr_ue_nas_t; 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 *nas_nrue_task(void *args_p); void *nas_nrue(void *args_p);