Commit d6e2bf16 authored by Guido Casati's avatar Guido Casati

Refactor registration accept decoding

previously we had decodeRegistrationAccept acting as a handler and calling
decode_registration_accept to do the decoding. However the logic was mixed
between the two functions, the naming was confusing and the code was hard
understand. The goal of this commit is to simplify the code, improve
readability and maintainability, while adapting the relevant function
to the new OAI NAS libs structure.

To this purpose:

* move enc/dec logic to NAS lib openair3/NAS/NR_UE/5GS/5GMM/MSG/RegistrationAccept.c -
  the logic of parse_allowed_nssai, get_allowed_nssai has been moved to
  decode_registration_accept and decode_nssai_ie
* add decode_nssai_ie and use it for both Allowed/Configured NSSAIs list (IE handling was added)
* do processing in the handler handle_registration_accept
* simplify the code by removing nested calls
* improve handling of PDU length

Currently only Registration Result, 5GS Mobile Identity, Allowed/Configured NSSAI
are decoded. The other optional IEIs are skipped (only the length is processed).
parent e842ca26
......@@ -32,37 +32,116 @@
#include <stdlib.h>
#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"
#define IEI_5G_GUTI 0x77
/**
* @brief Allowed NSSAI from Registration Accept according to 3GPP TS 24.501 Table 8.2.7.1.1
*/
static int decode_nssai_ie(nr_nas_msg_snssai_t *nssai, const uint8_t *buf)
{
const int length = *buf; // Length of S-NSSAI IE contents (list of S-NSSAIs)
const uint32_t decoded = length + 1; // Length IE (1 octet)
buf++;
int nssai_cnt = 0;
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:
item->sst = *buf++;
nssai_cnt++;
break;
case 2:
item->sst = *buf++;
item->hplmn_sst = *buf++;
nssai_cnt++;
break;
case 4:
item->sst = *buf++;
item->sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
nssai_cnt++;
break;
case 5:
item->sst = *buf++;
item->sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
item->hplmn_sst = *buf++;
nssai_cnt++;
break;
case 8:
item->sst = *buf++;
item->sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
item->hplmn_sst = *buf++;
item->hplmn_sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
nssai_cnt++;
break;
default:
PRINT_NAS_ERROR("Unknown length in Allowed S-NSSAI list item\n");
break;
}
}
return decoded;
}
int decode_registration_accept(registration_accept_msg *registration_accept, const uint8_t *buffer, uint32_t len)
{
uint32_t decoded = 0;
int decoded_result = 0;
int dec = 0;
const uint8_t *end = buffer + len;
/* Decoding mandatory fields */
if ((decoded_result =
decode_fgs_registration_result(&registration_accept->fgsregistrationresult, 0, *(buffer + decoded), len - decoded))
< 0)
return decoded_result;
if ((dec = decode_fgs_registration_result(&registration_accept->fgsregistrationresult, 0, *buffer, len)) < 0)
return dec;
buffer += dec;
decoded += decoded_result;
if (decoded < len && buffer[decoded] == 0x77) {
registration_accept->guti = calloc(1, sizeof(*registration_accept->guti));
if (!registration_accept->guti)
return -1;
int mi_dec = decode_5gs_mobile_identity(registration_accept->guti, 0x77, buffer + decoded, len - decoded);
if (mi_dec < 0)
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) {
PRINT_NAS_ERROR("Failed to decode 5GS Mobile Identity in Registration Accept\n");
return -1;
decoded += mi_dec;
}
buffer += dec;
}
// todo ,Decoding optional fields
return decoded;
// Allowed NSSAI (O)
/* Optional Presence IEs */
while (buffer < end) {
const int iei = *buffer++;
switch (iei) {
case 0x15: // allowed NSSAI
dec = decode_nssai_ie(registration_accept->nas_allowed_nssai, buffer);
buffer += dec;
break;
case 0x31: // configured NSSAI
dec = decode_nssai_ie(registration_accept->config_nssai, buffer);
buffer += dec;
break;
default:
dec = *buffer++; // content length + 1 byte (Length IE)
buffer += dec;
break;
}
}
return len;
}
int encode_registration_accept(const registration_accept_msg *registration_accept, uint8_t *buffer, uint32_t len)
......
......@@ -40,6 +40,14 @@
#ifndef REGISTRATION_ACCEPT_H_
#define REGISTRATION_ACCEPT_H_
// 9.11.3.37 of 3GPP TS 24.501
typedef struct {
int sst;
int hplmn_sst;
int sd;
int hplmn_sd;
} nr_nas_msg_snssai_t;
/*
* Message name: Registration accept
* Description: The REGISTRATION ACCEPT message is sent by the AMF to the UE. See table 8.2.7.1.1.
......@@ -52,6 +60,10 @@ typedef struct registration_accept_msg_tag {
FGSRegistrationResult fgsregistrationresult;
/* Optional fields */
FGSMobileIdentity *guti;
// Allowed NSSAI (O)
nr_nas_msg_snssai_t nas_allowed_nssai[8];
// Configured NSSAI (O)
nr_nas_msg_snssai_t config_nssai[8];
} registration_accept_msg;
int decode_registration_accept(registration_accept_msg *registrationaccept, const uint8_t *buffer, uint32_t len);
......
......@@ -69,7 +69,6 @@
extern uint16_t NB_UE_INST;
static nr_ue_nas_t nr_ue_nas[MAX_NAS_UE] = {0};
static nr_nas_msg_snssai_t nas_allowed_nssai[8];
#define FOREACH_STATE(TYPE_DEF) \
TYPE_DEF(NAS_SECURITY_NO_SECURITY_CONTEXT, 0) \
......@@ -737,30 +736,6 @@ static void handle_security_mode_command(nr_ue_nas_t *nas, as_nas_info_t *initia
generateSecurityModeComplete(nas, initialNasMsg);
}
static void decodeRegistrationAccept(const uint8_t *buf, int len, nr_ue_nas_t *nas)
{
registration_accept_msg reg_acc = {0};
/* it seems there is no 5G corresponding emm_msg_decode() function, so here
* we just jump to the right decision */
buf += 7; /* skip security header */
buf += 2; /* skip prot discriminator, security header, half octet */
AssertFatal(*buf == 0x42, "this is not a NAS Registration Accept\n");
buf++;
int decoded = decode_registration_accept(&reg_acc, buf, len);
AssertFatal(decoded > 0, "could not decode registration accept\n");
if (reg_acc.guti) {
AssertFatal(reg_acc.guti->guti.typeofidentity == FGS_MOBILE_IDENTITY_5G_GUTI,
"registration accept 5GS Mobile Identity is not GUTI, but %d\n",
reg_acc.guti->guti.typeofidentity);
nas->guti = malloc(sizeof(*nas->guti));
AssertFatal(nas->guti, "out of memory\n");
*nas->guti = reg_acc.guti->guti;
free(reg_acc.guti); /* no proper memory management for NAS decoded messages */
} else {
LOG_W(NAS, "no GUTI in registration accept\n");
}
}
static void generateRegistrationComplete(nr_ue_nas_t *nas,
as_nas_info_t *initialNasMsg,
SORTransparentContainer *sortransparentcontainer)
......@@ -1185,109 +1160,13 @@ static void send_nas_detach_req(nr_ue_nas_t *nas, bool wait_release)
itti_send_msg_to_task(TASK_RRC_NRUE, nas->UE_id, msg);
}
static void parse_allowed_nssai(nr_nas_msg_snssai_t nssaiList[8], const uint8_t *buf, const uint32_t len)
{
int nssai_cnt = 0;
const uint8_t *end = buf + len;
while (buf < end) {
const int length = *buf++;
nr_nas_msg_snssai_t *nssai = nssaiList + nssai_cnt;
nssai->sd = 0xffffff;
switch (length) {
case 1:
nssai->sst = *buf++;
nssai_cnt++;
break;
case 2:
nssai->sst = *buf++;
nssai->hplmn_sst = *buf++;
nssai_cnt++;
break;
case 4:
nssai->sst = *buf++;
nssai->sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
nssai_cnt++;
break;
case 5:
nssai->sst = *buf++;
nssai->sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
nssai->hplmn_sst = *buf++;
nssai_cnt++;
break;
case 8:
nssai->sst = *buf++;
nssai->sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
nssai->hplmn_sst = *buf++;
nssai->hplmn_sd = 0xffffff & ntoh_int24_buf(buf);
buf += 3;
nssai_cnt++;
break;
default:
LOG_E(NAS, "UE received unknown length in an allowed S-NSSAI\n");
break;
}
}
}
/* Extract Allowed NSSAI from Regestration Accept according to
3GPP TS 24.501 Table 8.2.7.1.1
*/
static void get_allowed_nssai(nr_nas_msg_snssai_t nssai[8], const uint8_t *pdu_buffer, const uint32_t pdu_length)
{
if ((pdu_buffer == NULL) || (pdu_length <= 0))
return;
const uint8_t *end = pdu_buffer + pdu_length;
if (((fgs_nas_message_security_header_t *)(pdu_buffer))->security_header_type > 0) {
pdu_buffer += SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH;
}
pdu_buffer += 1 + 1 + 1 + 2; // Mandatory fields offset
/* optional fields */
while (pdu_buffer < end) {
const int type = *pdu_buffer++;
int length = 0;
switch (type) {
case 0x77: // 5GS mobile identity
pdu_buffer += ntoh_int16_buf(pdu_buffer) + sizeof(uint16_t);
break;
case 0x4A: // PLMN list
case 0x54: // 5GS tracking area identity
pdu_buffer += *pdu_buffer + 1; // offset length + 1 byte which contains the length
break;
case 0x15: // allowed NSSAI
length = *pdu_buffer++;
parse_allowed_nssai(nssai, pdu_buffer, length);
pdu_buffer += length;
break;
default:
LOG_W(NAS, "This NAS IEI (0x%2.2x) is not handled when extracting list of allowed NSSAI\n", type);
length = *pdu_buffer++;
pdu_buffer += length;
break;
}
}
}
static void request_default_pdusession(nr_ue_nas_t *nas, int nssai_idx)
static void request_default_pdusession(nr_ue_nas_t *nas)
{
MessageDef *message_p = itti_alloc_new_message(TASK_NAS_NRUE, nas->UE_id, NAS_PDU_SESSION_REQ);
NAS_PDU_SESSION_REQ(message_p).pdusession_id = 10; /* first or default pdu session */
NAS_PDU_SESSION_REQ(message_p).pdusession_type = 0x91; // 0x91 = IPv4, 0x92 = IPv6, 0x93 = IPv4v6
NAS_PDU_SESSION_REQ(message_p).sst = nas_allowed_nssai[nssai_idx].sst;
NAS_PDU_SESSION_REQ(message_p).sd = nas_allowed_nssai[nssai_idx].sd;
NAS_PDU_SESSION_REQ(message_p).sst = nas->uicc->nssai_sst;
NAS_PDU_SESSION_REQ(message_p).sd = nas->uicc->nssai_sd;
itti_send_msg_to_task(TASK_NAS_NRUE, nas->UE_id, message_p);
}
......@@ -1308,11 +1187,52 @@ void *nas_nrue_task(void *args_p)
}
}
static void process_guti(Guti5GSMobileIdentity_t *guti, nr_ue_nas_t *nas)
{
AssertFatal(guti->typeofidentity == FGS_MOBILE_IDENTITY_5G_GUTI,
"registration accept 5GS Mobile Identity is not GUTI, but %d\n",
guti->typeofidentity);
nas->guti = malloc_or_fail(sizeof(*nas->guti));
*nas->guti = *guti;
}
static void handle_registration_accept(nr_ue_nas_t *nas, const uint8_t *pdu_buffer, uint32_t msg_length)
{
LOG_I(NAS, "[UE] Received REGISTRATION ACCEPT message\n");
decodeRegistrationAccept(pdu_buffer, msg_length, nas);
get_allowed_nssai(nas_allowed_nssai, pdu_buffer, msg_length);
registration_accept_msg msg = {0};
fgs_nas_message_security_header_t sp_header = {0};
const uint8_t *end = pdu_buffer + msg_length;
// security protected header
int decoded = decode_5gs_security_protected_header(&sp_header, pdu_buffer, msg_length);
if (!decoded) {
LOG_E(NAS, "NAS Registration Accept: failed to decode security protected header\n");
return;
}
pdu_buffer += decoded;
// plain header
fgmm_msg_header_t mm_header = {0};
if ((decoded = decode_5gmm_msg_header(&mm_header, pdu_buffer, end - pdu_buffer)) < 0) {
LOG_E(NAS, "Failed to decode NAS Registration Accept\n");
return;
}
if (mm_header.message_type != FGS_REGISTRATION_ACCEPT) {
LOG_E(NAS, "Failed to process NAS Registration Accept: wrong message type\n");
return;
}
pdu_buffer += decoded;
// plain payload
if ((decoded = decode_registration_accept(&msg, pdu_buffer, end - pdu_buffer)) < 0) {
LOG_E(NAS, "Failed to decode NAS Registration Accept\n");
return;
}
pdu_buffer += decoded;
// process GUTI
if (msg.guti) {
process_guti(&msg.guti->guti, nas);
free(msg.guti);
} else {
LOG_W(NAS, "no GUTI in registration accept\n");
}
as_nas_info_t initialNasMsg = {0};
generateRegistrationComplete(nas, &initialNasMsg, NULL);
......@@ -1320,11 +1240,10 @@ static void handle_registration_accept(nr_ue_nas_t *nas, const uint8_t *pdu_buff
send_nas_uplink_data_req(nas, &initialNasMsg);
LOG_I(NAS, "Send NAS_UPLINK_DATA_REQ message(RegistrationComplete)\n");
}
const int nssai_idx = get_user_nssai_idx(nas_allowed_nssai, nas);
if (nssai_idx < 0) {
if (get_user_nssai_idx(msg.nas_allowed_nssai, nas) < 0) {
LOG_E(NAS, "NSSAI parameters not match with allowed NSSAI. Couldn't request PDU session.\n");
} else {
request_default_pdusession(nas, nssai_idx);
request_default_pdusession(nas);
}
}
......
......@@ -51,14 +51,6 @@
/* 3GPP TS 24.501: 9.11.3.50 Service type */
#define SERVICE_TYPE_DATA 0x1
/* List of allowed NSSAI from NAS messaging. */
typedef struct {
int sst;
int hplmn_sst;
int sd;
int hplmn_sd;
} nr_nas_msg_snssai_t;
/* Security Key for SA UE */
typedef struct {
uint8_t kausf[32];
......
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