Merge remote-tracking branch 'origin/nr-ue-nas-service-accept' into integration_2025_w14_p1 (!3247)

Add enc/dec lib and unit test for 5GS NAS Service Accept

- add new files for Service Accept fgmm_service_accept.c/.h containing
  the enc/dec functions (8.2.17 3GPP TS 24.501)
- enabled compiling
- add unit test
- add handler in stack
This commit is contained in:
Robert Schmidt
2025-04-03 10:09:53 +02:00
10 changed files with 678 additions and 49 deletions
@@ -1,5 +1,7 @@
add_library(fgs_5gmm_lib OBJECT
fgmm_lib.c
fgs_service_request.c
fgmm_service_accept.c
RegistrationRequest.c
RegistrationAccept.c
FGSIdentityResponse.c
+152
View File
@@ -0,0 +1,152 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "fgmm_lib.h"
#include <string.h>
#include <arpa/inet.h> // For htons and ntohs
#include <stdlib.h> // For malloc and free
#include "common/utils/ds/byte_array.h"
#define GPRS_TIMER_LENGTH 3 // octets
/**
* Encode PDU Session Status and PDU session reactivation result (optional IEs)
* According to 9.11.3.42 and 9.11.3.44 of 3GPP TS 24.501, both IEs share the same
* structure, therefore this function covers them both.
* @param iei IE identifier to distinguish between PDU Session Status and PDU session reactivation result.
* @param psi pointer to PDU Session Status IE buffer to be encoded: index i corresponds to PDU Session i
*/
int encode_pdu_session_ie(byte_array_t *buffer, nas_service_IEI_t iei, const uint8_t *psi)
{
if (buffer->len < MIN_PDU_SESSION_CONTENTS_LEN + 2) {
PRINT_NAS_ERROR("%s failed: buffer is too small", __func__);
return -1;
}
uint8_t *buf = buffer->buf;
int encoded = 0;
buf[encoded++] = iei;
buf[encoded++] = MIN_PDU_SESSION_CONTENTS_LEN; // no spare octets
// Initialize the PDU session status octets to zero
buf[encoded] = 0x00;
buf[encoded + 1] = 0x00;
/* Encode Octet 3 (PSI 1-7) and Octet 4 (PSI 8-15).
Set each PSI bit according to the psi array containing the content IEs.
Start from bit 1 of octet 3 (i.e. PSI(0)), which is spare and shall be coded as zero.
The enabled bit translates to either PDU Session Active or PDU Session Reactivation Result */
for (int i = 1; i < 8; i++) { // skip spare bit
buf[encoded] |= psi[i] << i;
}
encoded++;
for (int i = 8; i < MAX_NUM_PSI; i++) {
buf[encoded] |= psi[i] << i % 8;
}
encoded++;
return encoded;
}
/**
* Decode PDU Session Status and PDU session reactivation result (optional IEs)
* According to 9.11.3.42 and 9.11.3.44 of 3GPP TS 24.501, both IEs share the same
* structure, therefore this function covers them both.
* @param psi pointer to buffer for the PDU Session Status IE to be decoded
*/
int decode_pdu_session_ie(uint8_t *psi, const byte_array_t *buffer)
{
int decoded = 0;
uint8_t *buf = buffer->buf;
if (buffer->len < MIN_PDU_SESSION_CONTENTS_LEN + 2) {
PRINT_NAS_ERROR("%s failed: buffer length is too small", __func__);
return -1;
}
// start decoding from length IE (IEI is decoded in the caller)
uint8_t pdu_session_status_len = buf[decoded++];
if (pdu_session_status_len > MAX_PDU_SESSION_CONTENTS_LEN) {
PRINT_NAS_ERROR("decoded length is out of bound for PDU Session Status/Reactivation result\n");
return -1;
}
/* Decode PDU Session Status content IEs bits in octet 3 (PSI 1-7).
Store the result psi array.
Skip bit 1 of octet 3 (i.e. PSI(0)), which is spare and shall be coded as zero. */
for (int i = 1; i < 8; i++) { // skip spare bit
psi[i] = (buf[decoded] >> i) & 0x01;
}
decoded++;
/* Decode PDU Session Status content IEs bits in octet 4 (PSI 8-15).
Store the result psi array. */
for (int i = 8; i < MAX_NUM_PSI; i++) {
psi[i] = (buf[decoded] >> i % 8) & 0x01;
}
decoded++;
return decoded;
}
/* Encode GPRS Timer IE (3GPP TS 24.008 10.5.7.3) */
int encode_gprs_timer_ie(byte_array_t *buffer, nas_service_IEI_t iei, const gprs_timer_t *timer)
{
if (buffer->len < GPRS_TIMER_LENGTH) {
PRINT_NAS_ERROR("Buffer length is too short to encode the GPRS Timer IE\n");
return -1;
}
uint8_t *buf = buffer->buf;
int encoded = 0;
// IEI
buf[encoded++] = iei;
// length
buf[encoded++] = 1; // length field (1 octet)
// Unit and value (1 octet)
uint8_t encoded_byte = 0;
encoded_byte |= (timer->unit & 0x07) << 5;
encoded_byte |= timer->value & 0x1F;
buf[encoded++] = encoded_byte;
return encoded;
}
/* Decode GPRS Timer IE (3GPP TS 24.008 10.5.7.3) */
int decode_gprs_timer_ie(gprs_timer_t *timer, const byte_array_t *buffer)
{
int decoded = 1; // Skip length ie (1 octet)
// Extract unit and value
uint8_t *buf = buffer->buf;
timer->unit = (buf[decoded] >> 5) & 0x07;
timer->value = buf[decoded++] & 0x1F;
return decoded;
}
/* Decode EAP Message (todo) */
int decode_eap_msg_ie(const byte_array_t *buffer)
{
int eap_msg_len = (buffer->buf[0] << 8) | buffer->buf[1];
PRINT_NAS_ERROR("EAP message IE in NAS Service Reject is not handled\n");
return eap_msg_len + 2; // content length + 2 octets for the length IE
}
+166
View File
@@ -0,0 +1,166 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*
* This header file defines structures, macros,
* and functions for the handling of common NAS 5GMM IEs.
* This library is intended for use within the 5GMM encode/decode library only.
*/
#ifndef FGS_LIB_H
#define FGS_LIB_H
#include <stdint.h>
#include <stdbool.h>
#include "fgs_nas_utils.h"
#include "common/utils/ds/byte_array.h"
/* The following defines are valide for
both PDU Session Status and PDU session Reactivation Result IEs */
#define MIN_PDU_SESSION_CONTENTS_LEN 2
#define MAX_PDU_SESSION_CONTENTS_LEN 32
#define MAX_NUM_PSI 16
#define MAX_NUM_PDU_ERRORS 256
/* This macro updates the local copy of
a byte_array_t pointer used for enc/dec */
#define UPDATE_BYTE_ARRAY(ba, shift) \
do { \
(ba).buf += (shift); \
(ba).len -= (shift); \
} while (0)
/* This macro checks the result of the encoder.
If successful, it updates the encoded bytes counter
and the local copy of the byte_array_t pointer.
Otherwise, it returns -1. */
#define ENCODE_NAS_IE(ba, exp, encoded) \
do { \
int ret = (exp); \
if (ret < 0) { \
return -1; \
} \
(encoded) += ret; \
UPDATE_BYTE_ARRAY((ba), ret); \
} while (0)
/* This macro checks the result of the decoder.
If successful, it updates the decoded bytes counter
and the local copy of the byte_array_t pointer.
Otherwise, it returns -1. */
#define DECODE_NAS_IE(ba, exp, decoded) \
do { \
int ret = (exp); \
if (ret < 0) { \
return -1; \
} \
(decoded) += ret; \
UPDATE_BYTE_ARRAY((ba), ret); \
} while (0)
/* Timer value is incremented in multiples of 2 seconds,
1 minute, etc (10.5.7.3 of 3GPP TS 24.008) */
#define FOREACH_TIMER_UNIT(TIMER_UNIT) \
TIMER_UNIT(TWO_SECONDS, 0x00) \
TIMER_UNIT(ONE_MINUTE, 0x01) \
TIMER_UNIT(DECIHOURS, 0x02) \
TIMER_UNIT(DEACTIVATED, 0x07)
typedef enum { FOREACH_TIMER_UNIT(TO_ENUM) } GPRS_Timer_t;
typedef enum { PDU_SESSION_INACTIVE = 0, PDU_SESSION_ACTIVE } PSI_status_t;
typedef enum { REACTIVATION_SUCCESS = 0, REACTIVATION_FAILED } PSI_reactivation_t;
#define FOREACH_SA_IEI(IEI_DEF) \
IEI_DEF(IEI_PDU_SESSION_STATUS, 0x50) \
IEI_DEF(IEI_PDU_SESSION_REACT_RESULT, 0x26) \
IEI_DEF(IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE, 0x72) \
IEI_DEF(IEI_EAPMSG, 0x78) \
IEI_DEF(IEI_CAG_INFO_LIST, 0x75) \
IEI_DEF(IEI_T3448_VALUE, 0x6B) \
IEI_DEF(IEI_T3446_VALUE, 0x5F)
// Enum for Service Accept IEIs
typedef enum { FOREACH_SA_IEI(TO_ENUM) } nas_service_IEI_t;
static const text_info_t sa_iei_s[] = {FOREACH_SA_IEI(TO_TEXT)};
// table 9.11.3.2.1
#define FOREACH_CAUSE(CAUSE_DEF) \
CAUSE_DEF(Illegal_UE, 0x3) \
CAUSE_DEF(PEI_not_accepted, 0x5) \
CAUSE_DEF(Illegal_ME, 0x6) \
CAUSE_DEF(SGS_services_not_allowed, 0x7) \
CAUSE_DEF(UE_identity_cannot_be_derived_by_the_network, 0x9) \
CAUSE_DEF(Implicitly_de_registered, 0x0a) \
CAUSE_DEF(PLMN_not_allowed, 0x0b) \
CAUSE_DEF(Tracking_area_not_allowed, 0x0c) \
CAUSE_DEF(Roaming_not_allowed_in_this_tracking_area, 0x0d) \
CAUSE_DEF(No_suitable_cells_in_tracking_area, 0x0f) \
CAUSE_DEF(MAC_failure, 0x14) \
CAUSE_DEF(Synch_failure, 0x15) \
CAUSE_DEF(Congestion, 0x16) \
CAUSE_DEF(UE_security_capabilities_mismatch, 0x17) \
CAUSE_DEF(Security_mode_rejected_unspecified, 0x18) \
CAUSE_DEF(Non_5G_authentication_unacceptable, 0x1a) \
CAUSE_DEF(N1_mode_not_allowed, 0x1b) \
CAUSE_DEF(Restricted_service_area, 0x1c) \
CAUSE_DEF(Redirection_to_EPC_required, 0x1f) \
CAUSE_DEF(LADN_not_available, 0x2b) \
CAUSE_DEF(No_network_slices_available, 0x3e) \
CAUSE_DEF(Maximum_number_of_PDU_sessions_reached, 0x41) \
CAUSE_DEF(Insufficient_resources_for_specific_slice_and_DNN, 0x43) \
CAUSE_DEF(Insufficient_resources_for_specific_slice, 0x45) \
CAUSE_DEF(ngKSI_already_in_use, 0x47) \
CAUSE_DEF(Non_3GPP_access_to_5GCN_not_allowed, 0x48) \
CAUSE_DEF(Serving_network_not_authorized, 0x49) \
CAUSE_DEF(Temporarily_not_authorized_for_this_SNPN, 0x4A) \
CAUSE_DEF(Permanently_not_authorized_for_this_SNPN, 0x4b) \
CAUSE_DEF(Not_authorized_for_this_CAG_or_authorized_for_CAG_cells_only, 0x4c) \
CAUSE_DEF(Wireline_access_area_not_allowed, 0x4d) \
CAUSE_DEF(Payload_was_not_forwarded, 0x5a) \
CAUSE_DEF(DNN_not_supported_or_not_subscribed_in_the_slice, 0x5b) \
CAUSE_DEF(Insufficient_user_plane_resources_for_the_PDU_session, 0x5c) \
CAUSE_DEF(Semantically_incorrect_message, 0x5f) \
CAUSE_DEF(Invalid_mandatory_information, 0x60) \
CAUSE_DEF(Message_type_non_existent_or_not_implemented, 0x61) \
CAUSE_DEF(Message_type_not_compatible_with_the_protocol_state, 0x62) \
CAUSE_DEF(Information_element_non_existent_or_not_implemented, 0x63) \
CAUSE_DEF(Conditional_IE_error, 0x64) \
CAUSE_DEF(Message_not_compatible_with_the_protocol_state, 0x65) \
CAUSE_DEF(Protocol_error_unspecified, 0x67)
typedef enum { FOREACH_CAUSE(TO_ENUM) } cause_id_t;
/* 10.5.7.3 3GPP TS 24.008 */
typedef struct {
uint8_t value;
uint8_t unit;
} gprs_timer_t;
int encode_pdu_session_ie(byte_array_t *buffer, nas_service_IEI_t iei, const uint8_t *psi);
int decode_pdu_session_ie(uint8_t *psi, const byte_array_t *buffer);
int encode_gprs_timer_ie(byte_array_t *buffer, nas_service_IEI_t iei, const gprs_timer_t *timer);
int decode_gprs_timer_ie(gprs_timer_t *timer, const byte_array_t *buffer);
int decode_eap_msg_ie(const byte_array_t *buffer);
#endif /* FGS_LIB_H */
@@ -0,0 +1,184 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "fgmm_service_accept.h"
#include <string.h>
#include <arpa/inet.h> // For htons and ntohs
#include <stdlib.h> // For malloc and free
#include "common/utils/utils.h" // For malloc_or_fail
#include "common/utils/ds/byte_array.h" // For byte_array_t
#include "fgmm_lib.h"
/** @brief Encode Service Accept (8.2.17 of 3GPP TS 24.501) */
int encode_fgs_service_accept(byte_array_t *buffer, const fgs_service_accept_msg_t *msg)
{
if (buffer->len < 1)
return -1; // No IEs present
byte_array_t ba = *buffer; // Local copy of buffer
uint32_t encoded = 0;
// Encode PDU Session Status (Optional)
if (msg->has_psi_status) {
ENCODE_NAS_IE(ba, encode_pdu_session_ie(&ba, IEI_PDU_SESSION_STATUS, msg->psi_status), encoded);
}
// Encode PDU Session Reactivation Result (Optional)
if (msg->has_psi_res) {
ENCODE_NAS_IE(ba, encode_pdu_session_ie(&ba, IEI_PDU_SESSION_REACT_RESULT, msg->psi_res), encoded);
}
// Encode PDU Session Reactivation Result Error Cause (Optional)
if (msg->num_errors) {
int start = encoded;
buffer->buf[encoded++] = IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE;
// encode length of PDU session reactivation result error cause
uint16_t error_len = msg->num_errors * sizeof(msg->cause[0]);
if (error_len > MAX_NUM_PDU_ERRORS) {
PRINT_NAS_ERROR("encoded length is out of bound (IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE)\n");
return -1;
}
uint16_t n_len = htons(error_len);
memcpy(&buffer->buf[encoded], &n_len, sizeof(n_len)); // length payload PDU Session Status
encoded += sizeof(error_len);
// encode PDU Session IDs and Causes
for (int i = 0; i < msg->num_errors; i++) {
buffer->buf[encoded++] = msg->cause[i].pdu_session_id;
buffer->buf[encoded++] = msg->cause[i].cause;
}
UPDATE_BYTE_ARRAY(ba, encoded - start);
}
/* Encode T3448 Timer (Optional)
3GPP TS 24.008 10.5.7.3 GPRS Timer */
if (msg->t3448) {
ENCODE_NAS_IE(ba, encode_gprs_timer_ie(&ba, IEI_T3448_VALUE, msg->t3448), encoded);
}
return encoded;
}
/** @brief Decode Service Accept (8.2.17 of 3GPP TS 24.501) */
int decode_fgs_service_accept(fgs_service_accept_msg_t *msg, const byte_array_t *buffer)
{
if (buffer->len < 1)
return -1; // nothing to decode
byte_array_t ba = *buffer; // Local copy of buffer (to be used in the decoding sub-functions)
uint32_t decoded = 0;
// Default output message before decoding
msg->has_psi_res = false;
msg->has_psi_status = false;
msg->num_errors = 0;
msg->t3448 = NULL;
// Decode Optional IEs
while (decoded < buffer->len) {
uint8_t iei = buffer->buf[decoded++];
UPDATE_BYTE_ARRAY(ba, 1);
switch (iei) {
case IEI_PDU_SESSION_STATUS:
msg->has_psi_status = true;
DECODE_NAS_IE(ba, decode_pdu_session_ie(msg->psi_status, &ba), decoded);
break;
case IEI_PDU_SESSION_REACT_RESULT:
msg->has_psi_res = true;
DECODE_NAS_IE(ba, decode_pdu_session_ie(msg->psi_res, &ba), decoded);
break;
case IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE: {
int start = decoded;
// Decode the length of the IE
uint16_t tmp;
memcpy(&tmp, &buffer->buf[decoded], sizeof(tmp));
uint16_t error_len = ntohs(tmp);
decoded += sizeof(error_len);
if (error_len > MAX_NUM_PDU_ERRORS * sizeof(msg->cause[0])) {
PRINT_NAS_ERROR("IEI_PDU_SESSION_REACT_RESULT_ERROR_CAUSE: decoded length is out of bound\n");
return -1;
}
// Decode each PDU Session ID and Cause
for (int i = 0; i < error_len; i += 2) {
msg->cause[msg->num_errors].pdu_session_id = buffer->buf[decoded++];
msg->cause[msg->num_errors].cause = buffer->buf[decoded++];
msg->num_errors++;
}
UPDATE_BYTE_ARRAY(ba, decoded - start);
} break;
case IEI_T3448_VALUE:
msg->t3448 = malloc_or_fail(sizeof(*msg->t3448));
DECODE_NAS_IE(ba, decode_gprs_timer_ie(msg->t3448, &ba), decoded);
break;
case IEI_EAPMSG:
DECODE_NAS_IE(ba, decode_eap_msg_ie(&ba), decoded);
break;
default:
PRINT_NAS_ERROR("Unkwown or invalid IEI %d\n", iei);
return -1;
}
}
return decoded;
}
/**
* @brief Equality check for Service Accept (enc/dec)
*/
bool eq_service_accept(const fgs_service_accept_msg_t *a, const fgs_service_accept_msg_t *b)
{
_NAS_EQ_CHECK_INT(a->has_psi_status, b->has_psi_status);
if (a->has_psi_status && b->has_psi_status) {
for (int i = 0; i < MAX_NUM_PSI; i++)
_NAS_EQ_CHECK_INT(a->psi_status[i], b->psi_status[i]);
}
_NAS_EQ_CHECK_INT(a->has_psi_res, b->has_psi_res);
if (a->has_psi_res && b->has_psi_res) {
for (int i = 0; i < MAX_NUM_PSI; i++)
_NAS_EQ_CHECK_INT(a->psi_res[i], b->psi_res[i]);
}
_NAS_EQ_CHECK_INT(a->num_errors, b->num_errors);
if (a->num_errors && b->num_errors) {
for (int i = 0; i < a->num_errors; i++) {
_NAS_EQ_CHECK_INT(a->cause[i].cause, b->cause[i].cause);
_NAS_EQ_CHECK_INT(a->cause[i].pdu_session_id, b->cause[i].pdu_session_id);
}
}
if (a->t3448 != NULL || b->t3448 != NULL) {
if (a->t3448 == NULL || b->t3448 == NULL) {
PRINT_NAS_ERROR("t3448 equality check failed\n");
return false;
}
_NAS_EQ_CHECK_INT(a->t3448->value, b->t3448->value);
_NAS_EQ_CHECK_INT(a->t3448->unit, b->t3448->unit);
}
return true;
}
void free_fgs_service_accept(fgs_service_accept_msg_t *msg)
{
free(msg->t3448);
}
@@ -0,0 +1,57 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef FGS_SERVICE_ACCEPT_H
#define FGS_SERVICE_ACCEPT_H
#include <stdint.h>
#include "fgs_nas_utils.h"
#include "common/utils/ds/byte_array.h"
#include "fgmm_lib.h"
typedef struct {
uint8_t pdu_session_id;
uint8_t cause;
} pdu_error_cause_t;
typedef struct {
// PDU Session Status (Optional)
uint8_t psi_status[MAX_NUM_PSI]; // index 0 is spare
bool has_psi_status;
// PDU Session Reactivation Result (Optional)
uint8_t psi_res[MAX_NUM_PSI]; // index 0 is spare
bool has_psi_res;
// PDU Session Reactivation Result Error Cause (Optional)
pdu_error_cause_t cause[MAX_NUM_PDU_ERRORS];
uint16_t num_errors;
// T3448 Value (Optional)
gprs_timer_t *t3448;
} fgs_service_accept_msg_t;
int decode_fgs_service_accept(fgs_service_accept_msg_t *msg, const byte_array_t *buffer);
int encode_fgs_service_accept(byte_array_t *buffer, const fgs_service_accept_msg_t *msg);
void free_fgs_service_accept(fgs_service_accept_msg_t *msg);
bool eq_service_accept(const fgs_service_accept_msg_t *a, const fgs_service_accept_msg_t *b);
#endif /* FGS_SERVICE_ACCEPT_H */
+2 -49
View File
@@ -25,6 +25,7 @@
#include <stdint.h>
#include "openair3/UICC/usim_interface.h"
#include "fgs_nas_utils.h"
#include "fgmm_lib.h"
#include "FGSAuthenticationResponse.h"
#include "FGSDeregistrationRequestUEOriginating.h"
#include "FGSIdentityResponse.h"
@@ -111,51 +112,6 @@ typedef enum { FOREACH_HEADER(TO_ENUM) } Security_header_t;
static const text_info_t security_header_type_s[] = {FOREACH_HEADER(TO_TEXT)};
// table 9.11.3.2.1
#define FOREACH_CAUSE(CAUSE_DEF) \
CAUSE_DEF(Illegal_UE,0x3 )\
CAUSE_DEF(PEI_not_accepted,0x5 )\
CAUSE_DEF(Illegal_ME,0x6 )\
CAUSE_DEF(SGS_services_not_allowed,0x7 )\
CAUSE_DEF(UE_identity_cannot_be_derived_by_the_network,0x9 )\
CAUSE_DEF(Implicitly_de_registered,0x0a )\
CAUSE_DEF(PLMN_not_allowed,0x0b )\
CAUSE_DEF(Tracking_area_not_allowed,0x0c )\
CAUSE_DEF(Roaming_not_allowed_in_this_tracking_area,0x0d )\
CAUSE_DEF(No_suitable_cells_in_tracking_area,0x0f )\
CAUSE_DEF(MAC_failure,0x14 )\
CAUSE_DEF(Synch_failure,0x15 )\
CAUSE_DEF(Congestion,0x16 )\
CAUSE_DEF(UE_security_capabilities_mismatch,0x17 )\
CAUSE_DEF(Security_mode_rejected_unspecified,0x18 )\
CAUSE_DEF(Non_5G_authentication_unacceptable,0x1a )\
CAUSE_DEF(N1_mode_not_allowed,0x1b )\
CAUSE_DEF(Restricted_service_area,0x1c )\
CAUSE_DEF(Redirection_to_EPC_required,0x1f )\
CAUSE_DEF(LADN_not_available,0x2b )\
CAUSE_DEF(No_network_slices_available,0x3e )\
CAUSE_DEF(Maximum_number_of_PDU_sessions_reached,0x41 )\
CAUSE_DEF(Insufficient_resources_for_specific_slice_and_DNN,0x43 )\
CAUSE_DEF(Insufficient_resources_for_specific_slice,0x45 )\
CAUSE_DEF(ngKSI_already_in_use,0x47 )\
CAUSE_DEF(Non_3GPP_access_to_5GCN_not_allowed,0x48 )\
CAUSE_DEF(Serving_network_not_authorized,0x49 )\
CAUSE_DEF(Temporarily_not_authorized_for_this_SNPN,0x4A )\
CAUSE_DEF(Permanently_not_authorized_for_this_SNPN,0x4b )\
CAUSE_DEF(Not_authorized_for_this_CAG_or_authorized_for_CAG_cells_only,0x4c )\
CAUSE_DEF(Wireline_access_area_not_allowed,0x4d )\
CAUSE_DEF(Payload_was_not_forwarded,0x5a )\
CAUSE_DEF(DNN_not_supported_or_not_subscribed_in_the_slice,0x5b )\
CAUSE_DEF(Insufficient_user_plane_resources_for_the_PDU_session,0x5c )\
CAUSE_DEF(Semantically_incorrect_message,0x5f )\
CAUSE_DEF(Invalid_mandatory_information,0x60 )\
CAUSE_DEF(Message_type_non_existent_or_not_implemented,0x61 )\
CAUSE_DEF(Message_type_not_compatible_with_the_protocol_state,0x62 )\
CAUSE_DEF(Information_element_non_existent_or_not_implemented,0x63 )\
CAUSE_DEF(Conditional_IE_error,0x64 )\
CAUSE_DEF(Message_not_compatible_with_the_protocol_state,0x65 )\
CAUSE_DEF(Protocol_error_unspecified,0x67 )
/* Map task id to printable name. */
#define CAUSE_TEXT(LabEl, nUmID) {nUmID, #LabEl},
@@ -163,11 +119,8 @@ static const text_info_t cause_text_info[] = {
FOREACH_CAUSE(TO_TEXT)
};
#define CAUSE_ENUM(LabEl, nUmID ) LabEl = nUmID,
#define CAUSE_ENUM(LabEl, nUmID) LabEl = nUmID,
//! Tasks id of each task
typedef enum {
FOREACH_CAUSE(TO_ENUM)
} cause_id_t;
//_table_9.11.4.2.1:_5GSM_cause_information_element
#define FOREACH_CAUSE_SECU(CAUSE_SECU_DEF) \
+1
View File
@@ -25,6 +25,7 @@
#include <stdint.h>
#include <arpa/inet.h>
#include <string.h> // For memcpy
#include <stdio.h>
#define PRINT_NAS_ERROR(...) fprintf(stderr, ##__VA_ARGS__)
@@ -2,7 +2,9 @@
#include <stdint.h>
#include <stdio.h>
#include <arpa/inet.h>
#include "common/utils/ds/byte_array.h"
#include "fgs_service_request.h"
#include "fgmm_service_accept.h"
#include "nr_nas_msg.h"
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
@@ -75,8 +77,59 @@ static void test_service_request(void)
"test_service_request() failed: original and decoded messages do not match\n");
}
/**
* @brief Test NAS Service Accept enc/dec
*/
static void test_service_accept(void)
{
// Dummy NAS Service Accept message
fgs_service_accept_msg_t orig = {
.has_psi_status = true,
.has_psi_res = true,
.num_errors = 1,
.t3448 = malloc_or_fail(sizeof(*orig.t3448)),
};
orig.psi_status[1] = PDU_SESSION_ACTIVE;
orig.psi_status[2] = PDU_SESSION_INACTIVE;
orig.psi_status[3] = PDU_SESSION_ACTIVE;
orig.psi_res[1] = REACTIVATION_FAILED;
orig.psi_res[2] = REACTIVATION_SUCCESS;
orig.cause[0].cause = Illegal_UE;
orig.cause[0].pdu_session_id = 1;
orig.t3448->value = 16;
orig.t3448->unit = TWO_SECONDS;
// Expected encoded data
uint8_t expected_enc[] = {0x50, 0x02, 0x0a, 0x00, 0x26, 0x02, 0x02, 0x00, 0x72, 0x00, 0x00, 0x01, 0x03, 0x6B, 0x01, 0x10};
uint16_t len_status = 2;
expected_enc[9] = (len_status >> 8) & 0xFF;
expected_enc[10] = len_status & 0xFF;
// Buffer
uint8_t buf[64] = {0};
byte_array_t buffer = {.buf = &buf[0], .len = sizeof(expected_enc)};
// Encode NAS Service Accept
int encoded_length = encode_fgs_service_accept(&buffer, &orig);
AssertFatal(encoded_length == sizeofArray(expected_enc), "encode_fgs_service_accept() failed\n");
// Compare the raw encoded buffer with expected encoded data
AssertFatal(memcmp(buffer.buf, expected_enc, buffer.len) == 0, "Encoding mismatch!\n");
// Decode NAS Service Accept
fgs_service_accept_msg_t dec = {0};
int decoded_length = decode_fgs_service_accept(&dec, &buffer);
AssertFatal(decoded_length >= 0, "decode_fgs_service_accept() failed\n");
// Compare original and decoded messages
AssertFatal(eq_service_accept(&orig, &dec), "test_service_accept() failed: original and decoded messages do not match\n");
free_fgs_service_accept(&dec);
free_fgs_service_accept(&orig);
}
int main()
{
test_service_request();
test_service_accept();
return 0;
}
+59
View File
@@ -36,6 +36,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common/utils/ds/byte_array.h"
#include "AuthenticationResponseParameter.h"
#include "FGCNasMessageContainer.h"
#include "FGSDeregistrationRequestUEOriginating.h"
@@ -64,6 +65,7 @@
#include "utils.h"
#include "openair2/SDAP/nr_sdap/nr_sdap.h"
#include "fgs_nas_utils.h"
#include "fgmm_service_accept.h"
#define MAX_NAS_UE 4
@@ -114,6 +116,16 @@ static void servingNetworkName(uint8_t *msg, char *imsiStr, int mnc_size)
snprintf((char *)msg, size, "5G:mnc%3s.mcc%3s.3gppnetwork.org", mnc, mcc);
}
static const char *print_info(uint8_t id, const text_info_t *array, uint8_t array_size)
{
for (uint8_t i = 0; i < array_size; i++) {
if (array[i].id == id) {
return array[i].text;
}
}
return "N/A";
}
static security_state_t nas_security_rx_process(nr_ue_nas_t *nas, uint8_t *pdu_buffer, int pdu_length)
{
if (nas->security_container == NULL)
@@ -1472,6 +1484,48 @@ static void handle_registration_accept(nr_ue_nas_t *nas, const uint8_t *pdu_buff
}
}
/* 3GPP TS 24.008 10.5.7.3 GPRS Timer */
static int process_gprs_timer(gprs_timer_t *timer)
{
if (!timer)
return -1;
int factor = 0;
switch (timer->unit) {
case TWO_SECONDS:
factor = 2;
break;
case ONE_MINUTE:
factor = 60;
break;
case DECIHOURS:
factor = 360; // 6 minutes
break;
case DEACTIVATED:
return -1;
default:
factor = 60; // default is 60 seconds
break;
}
return timer->value * factor;
}
static void handle_service_accept(nr_ue_nas_t *nas, const byte_array_t *buffer)
{
LOG_I(NAS, "Received NAS Service Accept message\n");
fgs_service_accept_msg_t msg = {0};
decode_fgs_service_accept(&msg, buffer);
// Extract timer t3448 in seconds (optional IE)
nas->t3448 = process_gprs_timer(msg.t3448);
// Extract possible reactivation errors
for (int i = 0; i < msg.num_errors; i++)
LOG_E(NAS,
"Received PDU Session %d reactivation result error cause %s\n",
msg.cause->pdu_session_id,
print_info(msg.cause->cause, cause_text_info, sizeofArray(cause_text_info)));
}
void *nas_nrue(void *args_p)
{
// Wait for a message or an event
@@ -1673,6 +1727,11 @@ void *nas_nrue(void *args_p)
LOG_E(NAS, "Received Registration reject cause: %s\n", cause_text_info[pdu_buffer[17]].text);
exit(1);
break;
case FGS_SERVICE_ACCEPT: {
byte_array_t buffer = {.buf = pdu_buffer, .len = pdu_length};
handle_service_accept(nas, &buffer);
break;
}
default:
LOG_W(NR_RRC, "unknown message type %d\n", msg_type);
break;
+2
View File
@@ -98,6 +98,8 @@ typedef struct {
bool is_rrc_inactive;
/* Timer T3512 */
int t3512;
// Timer t3448 in seconds (-1 = disabled)
int t3448;
} nr_ue_nas_t;
nr_ue_nas_t *get_ue_nas_info(module_id_t module_id);