Compare commits

...

5 Commits

Author SHA1 Message Date
Guido Casati
f59dd18a49 Add regression test for NAS Registration Accept
* the goal of this test is to prevent on the decoding
  of the NAS Registration Accept sent by the OAI CN5G
2025-04-07 17:35:01 +02:00
Guido Casati
ec1ff035fb Free NAS Registration Accept after use
* heap memory is allocated for this message during decoding
  and shall be free after processing
2025-04-07 17:33:41 +02:00
Guido Casati
dfe2f7da74 Add equality check and free to NAS Registration Accept library 2025-04-07 17:33:41 +02:00
Guido Casati
dfa7047a33 Remove harcoded Slice Differentiator (SD) in NAS decode_nssai_ie and update the processing logic
The SD stored in the allowed S-NSSAI list was hardcoded in the decoding
function, however this IE is not mandatory but optional, therefore
the check of the decoded message contents should take into account
this and check the stored value against the one from the UICC only when the field is present.
If no SD was received, consider the UICC SD a valid one.
2025-04-07 17:33:41 +02:00
Guido Casati
d9d61a8b26 Cleanup and other fixes to decode_registration_accept
* add check on minimum buffer length
* improve comments
* remove call to LOG utility functions
* replace magic numbers with defines
* refined includes
2025-04-07 17:33:41 +02:00
4 changed files with 177 additions and 29 deletions

View File

@@ -33,13 +33,12 @@
#include <string.h>
#include <stdint.h>
#include "conversions.h"
#include "TLVEncoder.h"
#include "TLVDecoder.h"
#include "RegistrationAccept.h"
#include "assertions.h"
#include "fgs_nas_utils.h"
#include "common/utils/utils.h"
#define IEI_5G_GUTI 0x77
#define REGISTRATION_ACCEPT_MIN_LEN 2 // (5GS registration result) 2 octets
/**
* @brief Allowed NSSAI from Registration Accept according to 3GPP TS 24.501 Table 8.2.7.1.1
@@ -53,7 +52,6 @@ static int decode_nssai_ie(nr_nas_msg_snssai_t *nssai, const uint8_t *buf)
const uint8_t *end = buf + length;
while (buf < end) {
nr_nas_msg_snssai_t *item = nssai + nssai_cnt;
item->sd = 0xffffff;
const int item_len = *buf++; // Length of S-NSSAI IE item
switch (item_len) {
case 1:
@@ -63,31 +61,38 @@ static int decode_nssai_ie(nr_nas_msg_snssai_t *nssai, const uint8_t *buf)
case 2:
item->sst = *buf++;
item->hplmn_sst = *buf++;
item->hplmn_sst = malloc_or_fail(sizeof(*item->hplmn_sst));
*item->hplmn_sst = *buf++;
nssai_cnt++;
break;
case 4:
item->sst = *buf++;
item->sd = 0xffffff & ntoh_int24_buf(buf);
item->sd = malloc_or_fail(sizeof(*item->sd));
*item->sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
nssai_cnt++;
break;
case 5:
item->sst = *buf++;
item->sd = 0xffffff & ntoh_int24_buf(buf);
item->sd = malloc_or_fail(sizeof(*item->sd));
*item->sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
item->hplmn_sst = *buf++;
item->hplmn_sst = malloc_or_fail(sizeof(*item->hplmn_sst));
*item->hplmn_sst = *buf++;
nssai_cnt++;
break;
case 8:
item->sst = *buf++;
item->sd = 0xffffff & ntoh_int24_buf(buf);
item->sd = malloc_or_fail(sizeof(*item->sd));
*item->sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
item->hplmn_sst = *buf++;
item->hplmn_sd = 0xffffff & ntoh_int24_buf(buf);
item->hplmn_sst = malloc_or_fail(sizeof(*item->hplmn_sst));
*item->hplmn_sst = *buf++;
item->hplmn_sd = malloc_or_fail(sizeof(*item->hplmn_sd));
*item->hplmn_sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
nssai_cnt++;
break;
@@ -105,11 +110,17 @@ int decode_registration_accept(registration_accept_msg *registration_accept, con
int dec = 0;
const uint8_t *end = buffer + len;
/* Decoding mandatory fields */
if (len < REGISTRATION_ACCEPT_MIN_LEN) {
PRINT_NAS_ERROR("%s: buffer length is too short.\n", __func__);
return -1;
}
// 5GS registration result (M)
if ((dec = decode_fgs_registration_result(&registration_accept->fgsregistrationresult, 0, *buffer, len)) < 0)
return dec;
buffer += dec;
// 5G-GUTI (O)
if (buffer < end && *buffer == IEI_5G_GUTI) {
registration_accept->guti = calloc_or_fail(1, sizeof(*registration_accept->guti));
if ((dec = decode_5gs_mobile_identity(registration_accept->guti, IEI_5G_GUTI, buffer, end - buffer)) < 0) {
@@ -119,8 +130,7 @@ int decode_registration_accept(registration_accept_msg *registration_accept, con
buffer += dec;
}
// Allowed NSSAI (O)
/* Optional Presence IEs */
// Other optional IEs
while (buffer < end) {
const int iei = *buffer++;
switch (iei) {
@@ -148,8 +158,6 @@ int encode_registration_accept(const registration_accept_msg *registration_accep
{
int encoded = 0;
LOG_FUNC_IN;
*(buffer + encoded) = encode_fgs_registration_result(&registration_accept->fgsregistrationresult);
encoded = encoded + 2;
@@ -160,6 +168,81 @@ int encode_registration_accept(const registration_accept_msg *registration_accep
encoded += mi_enc;
}
// todo ,Encoding optional fields
LOG_FUNC_RETURN(encoded);
return encoded;
}
/** Equality check for NAS Registration Accept */
bool eq_snssai(const nr_nas_msg_snssai_t *a, const nr_nas_msg_snssai_t *b)
{
_NAS_EQ_CHECK_INT(a->sst, b->sst);
if (a->hplmn_sst && b->hplmn_sst)
_NAS_EQ_CHECK_INT(*a->hplmn_sst, *b->hplmn_sst);
if (a->sd && b->sd)
_NAS_EQ_CHECK_INT(*a->sd, *b->sd);
if (a->hplmn_sd && b->hplmn_sd)
_NAS_EQ_CHECK_INT(*a->hplmn_sd, *b->hplmn_sd);
return true;
}
bool eq_fgmm_registration_accept(const registration_accept_msg *a, const registration_accept_msg *b)
{
if (!a || !b) {
PRINT_NAS_ERROR("Null pointer in registration_accept_msg_eq\n");
return false;
}
// fgsregistrationresult (M)
_NAS_EQ_CHECK_INT(a->fgsregistrationresult.iei, b->fgsregistrationresult.iei);
_NAS_EQ_CHECK_INT(a->fgsregistrationresult.resultlength, b->fgsregistrationresult.resultlength);
_NAS_EQ_CHECK_INT(a->fgsregistrationresult.spare, b->fgsregistrationresult.spare);
_NAS_EQ_CHECK_INT(a->fgsregistrationresult.smsallowed, b->fgsregistrationresult.smsallowed);
_NAS_EQ_CHECK_INT(a->fgsregistrationresult.registrationresult, b->fgsregistrationresult.registrationresult);
// GUTI (O)
if (a->guti && b->guti) {
_NAS_EQ_CHECK_INT(a->guti->guti.spare, b->guti->guti.spare);
_NAS_EQ_CHECK_INT(a->guti->guti.oddeven, b->guti->guti.oddeven);
_NAS_EQ_CHECK_INT(a->guti->guti.typeofidentity, b->guti->guti.typeofidentity);
_NAS_EQ_CHECK_INT(a->guti->guti.mccdigit2, b->guti->guti.mccdigit2);
_NAS_EQ_CHECK_INT(a->guti->guti.mccdigit1, b->guti->guti.mccdigit1);
_NAS_EQ_CHECK_INT(a->guti->guti.mncdigit3, b->guti->guti.mncdigit3);
_NAS_EQ_CHECK_INT(a->guti->guti.mccdigit3, b->guti->guti.mccdigit3);
_NAS_EQ_CHECK_INT(a->guti->guti.mncdigit2, b->guti->guti.mncdigit2);
_NAS_EQ_CHECK_INT(a->guti->guti.mncdigit1, b->guti->guti.mncdigit1);
_NAS_EQ_CHECK_INT(a->guti->guti.amfregionid, b->guti->guti.amfregionid);
_NAS_EQ_CHECK_INT(a->guti->guti.amfsetid, b->guti->guti.amfsetid);
_NAS_EQ_CHECK_INT(a->guti->guti.amfpointer, b->guti->guti.amfpointer);
_NAS_EQ_CHECK_INT(a->guti->guti.tmsi, b->guti->guti.tmsi);
} else if (a->guti || b->guti) {
PRINT_NAS_ERROR("NAS Equality Check failure: One of the two GUTIs is NULL\n");
return false;
}
// Allowed NSSAI (O), Configured NSSAI(O)
for (int i = 0; i < NAS_MAX_NUMBER_SLICES; i++) {
eq_snssai(&a->config_nssai[i], &b->config_nssai[i]);
eq_snssai(&a->nas_allowed_nssai[i], &b->nas_allowed_nssai[i]);
}
return true;
}
/** Memory management of NAS Registration Accept */
void free_nssai(nr_nas_msg_snssai_t *msg)
{
free(msg->hplmn_sd);
free(msg->hplmn_sst);
free(msg->sd);
}
void free_fgmm_registration_accept(registration_accept_msg *msg)
{
free(msg->guti);
for (int i = 0; i < NAS_MAX_NUMBER_SLICES; i++) {
free_nssai(&msg->nas_allowed_nssai[i]);
free_nssai(&msg->config_nssai[i]);
}
}

View File

@@ -31,6 +31,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "SecurityHeaderType.h"
#include "MessageType.h"
@@ -40,12 +41,14 @@
#ifndef REGISTRATION_ACCEPT_H_
#define REGISTRATION_ACCEPT_H_
#define NAS_MAX_NUMBER_SLICES 8
// 9.11.3.37 of 3GPP TS 24.501
typedef struct {
int sst;
int hplmn_sst;
int sd;
int hplmn_sd;
int *hplmn_sst;
int *sd;
int *hplmn_sd;
} nr_nas_msg_snssai_t;
/*
@@ -56,18 +59,21 @@ typedef struct {
*/
typedef struct registration_accept_msg_tag {
/* Mandatory fields */
// 5GS registration result (M)
FGSRegistrationResult fgsregistrationresult;
/* Optional fields */
// 5G-GUTI (O)
FGSMobileIdentity *guti;
// Allowed NSSAI (O)
nr_nas_msg_snssai_t nas_allowed_nssai[8];
nr_nas_msg_snssai_t nas_allowed_nssai[NAS_MAX_NUMBER_SLICES];
// Configured NSSAI (O)
nr_nas_msg_snssai_t config_nssai[8];
nr_nas_msg_snssai_t config_nssai[NAS_MAX_NUMBER_SLICES];
} registration_accept_msg;
int decode_registration_accept(registration_accept_msg *registrationaccept, const uint8_t *buffer, uint32_t len);
int encode_registration_accept(const registration_accept_msg *registrationaccept, uint8_t *buffer, uint32_t len);
bool eq_fgmm_registration_accept(const registration_accept_msg *a, const registration_accept_msg *b);
void free_fgmm_registration_accept(registration_accept_msg *msg);
#endif /* ! defined(REGISTRATION_ACCEPT_H_) */

View File

@@ -3,6 +3,7 @@
#include <stdio.h>
#include <arpa/inet.h>
#include "common/utils/ds/byte_array.h"
#include "RegistrationAccept.h"
#include "fgs_service_request.h"
#include "fgmm_service_accept.h"
#include "nr_nas_msg.h"
@@ -25,6 +26,59 @@ static bool eq_service_request(const fgs_service_request_msg_t *a, const fgs_ser
return result;
}
/** @brief Test regression of NAS Registration Accept decoding
* decode the message received from OAI CN5G
* and compare with the expected one */
static void test_regression_registration_accept(void)
{
// Registration Accept message
registration_accept_msg orig = {
.guti = malloc_or_fail(sizeof(*orig.guti)),
.fgsregistrationresult.registrationresult = 0x01,
.nas_allowed_nssai[0].sst = 0x01,
.nas_allowed_nssai[1].sst = 0x01,
.nas_allowed_nssai[2].sst = 0x01,
.config_nssai[0].sst = 0x01,
.config_nssai[1].sst = 0x01,
.config_nssai[2].sst = 0x01,
};
orig.guti->guti.typeofidentity = FGS_MOBILE_IDENTITY_5G_GUTI;
orig.guti->guti.amfpointer = 0x01;
orig.guti->guti.amfregionid = 0x01;
orig.guti->guti.amfsetid = 0x01;
orig.guti->guti.mccdigit1 = 0x00;
orig.guti->guti.mccdigit2 = 0x00;
orig.guti->guti.mccdigit3 = 0x01;
orig.guti->guti.mncdigit1 = 0x00;
orig.guti->guti.mncdigit2 = 0x01;
orig.guti->guti.mncdigit3 = 0x0F;
orig.guti->guti.tmsi = 0xb0207806;
orig.guti->guti.spare = 0b1111;
orig.guti->guti.oddeven = 0;
// Encoded Registration Accept message from OAI CN5G
uint8_t cn5g_msg[] = {0x01, 0x01, // Registration Result
0x77, 0x00, 0x0B, 0xF2, 0x00, 0xF1, 0x10, // 5G-GUTI
0x01, 0x00, 0x41, 0xB0, 0x20, 0x78, 0x06, // 5G-GUTI
0x15, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // Allowed N-NSSAI
0x31, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; // Configured N-NSSAI
// Decode NAS Registration Accept
int encoded_length;
registration_accept_msg decoded = {0};
AssertFatal((encoded_length = decode_registration_accept(&decoded, cn5g_msg, sizeof(cn5g_msg))) >= 0,
"encode_fgs_service_request() failed\n");
// Compare the raw encoded buffer with expected encoded data
bool ret = eq_fgmm_registration_accept((const registration_accept_msg *)&orig, (const registration_accept_msg *)&decoded);
AssertFatal(ret, "Encoding mismatch!\n");
// Free dynamic allocated memory
free_fgmm_registration_accept(&orig);
free_fgmm_registration_accept(&decoded);
}
/**
* @brief Test NAS Service Request enc/dec
*/
@@ -129,6 +183,7 @@ static void test_service_accept(void)
int main()
{
test_regression_registration_accept();
test_service_request();
test_service_accept();
return 0;

View File

@@ -1409,11 +1409,14 @@ static void request_default_pdusession(nr_ue_nas_t *nas)
itti_send_msg_to_task(TASK_NAS_NRUE, nas->UE_id, message_p);
}
static int get_user_nssai_idx(const nr_nas_msg_snssai_t allowed_nssai[8], const nr_ue_nas_t *nas)
static int get_user_nssai_idx(const nr_nas_msg_snssai_t allowed_nssai[NAS_MAX_NUMBER_SLICES], const nr_ue_nas_t *nas)
{
for (int i = 0; i < 8; i++) {
for (int i = 0; i < NAS_MAX_NUMBER_SLICES; i++) {
const nr_nas_msg_snssai_t *nssai = allowed_nssai + i;
if ((nas->uicc->nssai_sst == nssai->sst) && (nas->uicc->nssai_sd == nssai->sd))
/* If it was received in Registration Accept, check the SD
in the stored Allowed N-SSAI, else, consider the SD valid */
bool sd_match = nssai->sd ? (nas->uicc->nssai_sd == *nssai->sd ? true : false) : true;
if ((nas->uicc->nssai_sst == nssai->sst) && sd_match)
return i;
}
return -1;
@@ -1468,7 +1471,6 @@ static void handle_registration_accept(nr_ue_nas_t *nas, const uint8_t *pdu_buff
// process GUTI
if (msg.guti) {
process_guti(&msg.guti->guti, nas);
free(msg.guti);
} else {
LOG_W(NAS, "no GUTI in registration accept\n");
}
@@ -1487,6 +1489,8 @@ static void handle_registration_accept(nr_ue_nas_t *nas, const uint8_t *pdu_buff
} else {
request_default_pdusession(nas);
}
// Free local message after processing
free_fgmm_registration_accept(&msg);
}
/* 3GPP TS 24.008 10.5.7.3 GPRS Timer */