From e11a09b674d366479b9eb5a9f016f80ca8853078 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 7 Feb 2025 10:08:21 +0100 Subject: [PATCH 1/5] Add include for stdio.h in fgs_nas_utils.h (fprintf) --- openair3/NAS/NR_UE/5GS/fgs_nas_utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/openair3/NAS/NR_UE/5GS/fgs_nas_utils.h b/openair3/NAS/NR_UE/5GS/fgs_nas_utils.h index 09754e9903..7df312a757 100644 --- a/openair3/NAS/NR_UE/5GS/fgs_nas_utils.h +++ b/openair3/NAS/NR_UE/5GS/fgs_nas_utils.h @@ -25,6 +25,7 @@ #include #include #include // For memcpy +#include #define PRINT_NAS_ERROR(...) fprintf(stderr, ##__VA_ARGS__) From 31f14027902196c7715ead401cdf1e96983be954 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 18 Feb 2025 10:48:52 +0100 Subject: [PATCH 2/5] Add common definitions to new fgmm_lib.c/.h files The 5GMM library in openair3/NAS/NR_UE/5GS/5GMM/ contains the all the definitions necessary to do the enc/dec of 5GMM IEs. 5GMM messages can share the same IEs, therefore they can use common definitions and enc/dec functions. The goal of this commit is to add common definitions to new shared 5GMM library files. The consumers of fgmm_lib are the functions defined in the openair3/NAS/NR_UE/5GS/5GMM/ library. --- .../NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt | 1 + openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c | 152 ++++++++++++++++ openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h | 166 ++++++++++++++++++ openair3/NAS/NR_UE/5GS/NR_NAS_defs.h | 51 +----- 4 files changed, 321 insertions(+), 49 deletions(-) create mode 100644 openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c create mode 100644 openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt index be84772da6..6288ece940 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(fgs_5gmm_lib OBJECT + fgmm_lib.c fgs_service_request.c RegistrationRequest.c RegistrationAccept.c diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c new file mode 100644 index 0000000000..e21bc141c4 --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c @@ -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 +#include // For htons and ntohs +#include // 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 +} diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h new file mode 100644 index 0000000000..b09f19b828 --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h @@ -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 +#include +#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 */ \ No newline at end of file diff --git a/openair3/NAS/NR_UE/5GS/NR_NAS_defs.h b/openair3/NAS/NR_UE/5GS/NR_NAS_defs.h index babda98aaa..94e7f38a57 100644 --- a/openair3/NAS/NR_UE/5GS/NR_NAS_defs.h +++ b/openair3/NAS/NR_UE/5GS/NR_NAS_defs.h @@ -25,6 +25,7 @@ #include #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) \ From 052491addedfa90503ad3629f015d9c2bd629df1 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 14 Nov 2024 23:48:25 +0100 Subject: [PATCH 3/5] Add enc/dec/eq/free functions for 5GS NAS Service Accept lib * add new files for Service Accept enc/dec fgmm_service_accept.c/.h * add to CMakeLists.txt --- .../NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt | 1 + .../NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c | 184 ++++++++++++++++++ .../NR_UE/5GS/5GMM/MSG/fgmm_service_accept.h | 57 ++++++ 3 files changed, 242 insertions(+) create mode 100644 openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c create mode 100644 openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.h diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt index 6288ece940..6947a3421c 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt @@ -1,6 +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 diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c new file mode 100644 index 0000000000..7130586326 --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c @@ -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 +#include // For htons and ntohs +#include // 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); +} diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.h new file mode 100644 index 0000000000..9f82f47eb1 --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.h @@ -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 +#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 */ From 35fd18a090b4fc8ee8f037029913216522d6549b Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 15 Nov 2024 16:02:28 +0100 Subject: [PATCH 4/5] Add test for NAS Service Accept enc/dec library --- openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c | 53 +++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c b/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c index 41865051cd..12e69c1581 100644 --- a/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c +++ b/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c @@ -2,7 +2,9 @@ #include #include #include +#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; } From a092e7ff3f06fe6299ff8446619207885ad9229d Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 28 Jan 2025 19:22:55 +0100 Subject: [PATCH 5/5] Handle NAS Service Accept in stack * also, add function to process GPRS timer --- openair3/NAS/NR_UE/nr_nas_msg.c | 59 +++++++++++++++++++++++++++++++++ openair3/NAS/NR_UE/nr_nas_msg.h | 2 ++ 2 files changed, 61 insertions(+) diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 64a541aa78..d750cb33f8 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -36,6 +36,7 @@ #include #include #include +#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; diff --git a/openair3/NAS/NR_UE/nr_nas_msg.h b/openair3/NAS/NR_UE/nr_nas_msg.h index c46ba6fcd4..3bba6b1cec 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.h +++ b/openair3/NAS/NR_UE/nr_nas_msg.h @@ -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);