From ab3bb363578f9bffcdc3224347cf39d5b689dadc Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 18 Feb 2025 11:15:00 +0100 Subject: [PATCH 1/5] Add eq_gprs_timer to common 5gmm_lib --- openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c | 8 ++++++++ openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h | 1 + 2 files changed, 9 insertions(+) diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c index e21bc141c4..323cffe7e7 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c @@ -143,6 +143,14 @@ int decode_gprs_timer_ie(gprs_timer_t *timer, const byte_array_t *buffer) return decoded; } +/** @brief Equality check for GPRS Timer */ +bool eq_gprs_timer(const gprs_timer_t *a, const gprs_timer_t *b) +{ + _NAS_EQ_CHECK_INT(a->value, b->value); + _NAS_EQ_CHECK_INT(a->unit, b->unit); + return true; +} + /* Decode EAP Message (todo) */ int decode_eap_msg_ie(const byte_array_t *buffer) { diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h index b09f19b828..4acf7368b5 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h @@ -161,6 +161,7 @@ int encode_pdu_session_ie(byte_array_t *buffer, nas_service_IEI_t iei, const uin 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); +bool eq_gprs_timer(const gprs_timer_t *a, const gprs_timer_t *b); int decode_eap_msg_ie(const byte_array_t *buffer); #endif /* FGS_LIB_H */ \ No newline at end of file From 25bc1bd89a18b30e7cda864546c9b31502ad6da0 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 4 Apr 2025 10:40:22 +0200 Subject: [PATCH 2/5] Adopt eq_gprs_timer in eq_service_accept --- openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 7130586326..797deede84 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c @@ -172,8 +172,8 @@ bool eq_service_accept(const fgs_service_accept_msg_t *a, const fgs_service_acce 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); + if (!eq_gprs_timer(a->t3448, b->t3448)) + return false; } return true; } From 5af4b8920303eecc14f8a3877453858feb5eeeb2 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 15 Nov 2024 19:16:51 +0100 Subject: [PATCH 3/5] Add enc/dec function for NAS Service Reject library --- .../NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt | 1 + .../NR_UE/5GS/5GMM/MSG/fgmm_service_reject.c | 154 ++++++++++++++++++ .../NR_UE/5GS/5GMM/MSG/fgmm_service_reject.h | 45 +++++ 3 files changed, 200 insertions(+) create mode 100644 openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.c create mode 100644 openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.h diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt index 6947a3421c..c1a28a18f3 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(fgs_5gmm_lib OBJECT fgmm_lib.c fgs_service_request.c fgmm_service_accept.c + fgmm_service_reject.c RegistrationRequest.c RegistrationAccept.c FGSIdentityResponse.c diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.c new file mode 100644 index 0000000000..74175fda36 --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.c @@ -0,0 +1,154 @@ +/* + * 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_reject.h" +#include +#include // For htons and ntohs +#include // For malloc and free +#include "common/utils/ds/byte_array.h" +#include "common/utils/utils.h" +#include "fgmm_lib.h" + +#define SERVICE_REJECT_MIN_LEN 1 // 1 octet + +/** @brief Encode Service Reject (8.2.18 of 3GPP TS 24.501) */ +int encode_fgs_service_reject(byte_array_t *buffer, const fgs_service_reject_msg_t *msg) +{ + if (buffer->len < SERVICE_REJECT_MIN_LEN) { + PRINT_NAS_ERROR("Failed to encode Service Reject: missing Cause IE!\n"); + return -1; + } + + byte_array_t ba = *buffer; // Local copy of buffer + int encoded = 0; + + // 5GMM cause (Mandatory) + buffer->buf[encoded++] = msg->cause; + UPDATE_BYTE_ARRAY(ba, 1); + + // 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); + } + + // T3446 Timer (Optional) + if (msg->t3446) { + ENCODE_NAS_IE(ba, encode_gprs_timer_ie(&ba, IEI_T3446_VALUE, msg->t3446), encoded); + } + + // T3448 Timer (Optional) + if (msg->t3448) { + ENCODE_NAS_IE(ba, encode_gprs_timer_ie(&ba, IEI_T3448_VALUE, msg->t3448), encoded); + } + + return encoded; +} + +/** @brief Decode Service Reject (8.2.18 of 3GPP TS 24.501) */ +int decode_fgs_service_reject(fgs_service_reject_msg_t *msg, const byte_array_t *buffer) +{ + if (buffer->len < SERVICE_REJECT_MIN_LEN) { + PRINT_NAS_ERROR("Nothing to decode: missing Cause IE!\n"); + return -1; + } + + byte_array_t ba = *buffer; // Local copy of buffer to be used in the decoding sub-functions + uint32_t decoded = 0; + + // Cause (Mandatory) + msg->cause = buffer->buf[decoded++]; + UPDATE_BYTE_ARRAY(ba, 1); + + // Decode Optional IEs + while (decoded < buffer->len) { + uint8_t iei = buffer->buf[decoded++]; + UPDATE_BYTE_ARRAY(ba, 1); // Skip IEI + + 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_T3448_VALUE: + msg->t3448 = malloc_or_fail(sizeof(*msg->t3448)); + DECODE_NAS_IE(ba, decode_gprs_timer_ie(msg->t3448, (const byte_array_t *)&ba), decoded); + break; + + case IEI_T3446_VALUE: + msg->t3446 = malloc_or_fail(sizeof(*msg->t3446)); + DECODE_NAS_IE(ba, decode_gprs_timer_ie(msg->t3446, (const byte_array_t *)&ba), decoded); + break; + + case IEI_EAPMSG: + DECODE_NAS_IE(ba, decode_eap_msg_ie((const byte_array_t *)&ba), decoded); + break; + + case IEI_CAG_INFO_LIST: { + int cag_list_len = (buffer->buf[decoded] << 8) | buffer->buf[decoded + 1]; + int cag_len = cag_list_len + 2; // encoded content + 2 octets for the length IE + decoded += cag_len; + UPDATE_BYTE_ARRAY(ba, cag_len); + PRINT_NAS_ERROR("CAG information list IE in NAS Service Reject is not handled\n"); + } break; + + default: + PRINT_NAS_ERROR("Uknown or invalid NAS Service Reject IEI %d\n", iei); + return -1; + } + } + + return decoded; +} + +/** @brief Equality check for Service Reject (enc/dec) */ +bool eq_service_reject(const fgs_service_reject_msg_t *a, const fgs_service_reject_msg_t *b) +{ + _NAS_EQ_CHECK_INT(a->cause, b->cause); + _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]); + } + if (a->t3448 != NULL || b->t3448 != NULL) { + if (a->t3448 == NULL || b->t3448 == NULL) { + PRINT_NAS_ERROR("t3448 equality check failed\n"); + return false; + } + if (!eq_gprs_timer(a->t3448, b->t3448)) + return false; + } + if (a->t3446 != NULL || b->t3446 != NULL) { + if (a->t3446 == NULL || b->t3446 == NULL) { + PRINT_NAS_ERROR("t3448 equality check failed\n"); + return false; + } + if (!eq_gprs_timer(a->t3446, b->t3446)) + return false; + } + return true; +} + +void free_fgs_service_reject(fgs_service_reject_msg_t *msg) +{ + free(msg->t3446); + free(msg->t3448); +} diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.h new file mode 100644 index 0000000000..61337d8525 --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.h @@ -0,0 +1,45 @@ +/* + * 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_reject_H +#define FGS_service_reject_H + +#include +#include "fgmm_lib.h" + +typedef struct { + // 5GMM cause (Mandatory) + cause_id_t cause; + // T3346 Value (Optional) + gprs_timer_t *t3446; + // PDU Session Status (Optional) + uint8_t psi_status[MAX_NUM_PSI]; + bool has_psi_status; + // T3448 Value (Optional) + gprs_timer_t *t3448; +} fgs_service_reject_msg_t; + +int decode_fgs_service_reject(fgs_service_reject_msg_t *msg, const byte_array_t *buffer); +int encode_fgs_service_reject(byte_array_t *buffer, const fgs_service_reject_msg_t *msg); +void free_fgs_service_reject(fgs_service_reject_msg_t *msg); +bool eq_service_reject(const fgs_service_reject_msg_t *a, const fgs_service_reject_msg_t *b); + +#endif /* FGS_service_reject_H */ From 2b9782caa1ebbc8136dae8e06ee13b01a4b3a80a Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 15 Nov 2024 19:18:13 +0100 Subject: [PATCH 4/5] Add unit test for Service Reject --- openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c | 44 +++++++++++++++++++++ 1 file changed, 44 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 12e69c1581..a73ee9b2be 100644 --- a/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c +++ b/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c @@ -5,6 +5,7 @@ #include "common/utils/ds/byte_array.h" #include "fgs_service_request.h" #include "fgmm_service_accept.h" +#include "fgmm_service_reject.h" #include "nr_nas_msg.h" void exit_function(const char *file, const char *function, const int line, const char *s, const int assert) @@ -127,9 +128,52 @@ static void test_service_accept(void) free_fgs_service_accept(&orig); } +/** @brief Test NAS Service Reject enc/dec */ +static void test_service_reject(void) +{ + // Dummy NAS Service Reject message + fgs_service_reject_msg_t orig = { + .cause = Illegal_UE, + .has_psi_status = true, + .t3446 = malloc_or_fail(sizeof(*orig.t3446)), + .t3448 = malloc_or_fail(sizeof(*orig.t3448)), + }; + orig.psi_status[1] = PDU_SESSION_INACTIVE; // PDU Session 1 + orig.psi_status[2] = PDU_SESSION_ACTIVE; // PDU Session 2 + orig.t3446->value = 10; + orig.t3446->unit = TWO_SECONDS; + orig.t3448->value = 1; + orig.t3448->unit = ONE_MINUTE; + + // Expected encoded data + uint8_t expected_enc[] = {0x03, 0x50, 0x02, 0x04, 0x00, 0x5F, 0x01, 0x0A, 0x6B, 0x01, 0x21}; + + // Buffer + uint8_t buf[64] = {0}; + byte_array_t buffer = {.buf = &buf[0], .len = sizeof(expected_enc)}; + + // Encode NAS Service Reject + int encoded_length = encode_fgs_service_reject(&buffer, &orig); + AssertFatal(encoded_length == sizeofArray(expected_enc), "encode_fgs_service_reject() failed: %d != %ld\n", encoded_length, sizeofArray(expected_enc)); + + // Compare the raw encoded buffer with expected encoded data + AssertFatal(memcmp(buffer.buf, expected_enc, buffer.len) == 0, "Encoding mismatch!\n"); + + // Decode NAS Service Reject + fgs_service_reject_msg_t dec = {0}; + int decoded_length = decode_fgs_service_reject(&dec, &buffer); + AssertFatal(decoded_length >= 0, "decode_fgs_service_reject() failed\n"); + + // Compare original and decoded messages + AssertFatal(eq_service_reject(&orig, &dec), "test_service_reject() failed: original and decoded messages do not match\n"); + free_fgs_service_reject(&dec); + free_fgs_service_reject(&orig); +} + int main() { test_service_request(); test_service_accept(); + test_service_reject(); return 0; } From 312f296b60caa6b9a645c5d62260477a8cc6d061 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 4 Apr 2025 11:11:36 +0200 Subject: [PATCH 5/5] Add NAS handler for Service Reject message --- openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h | 2 ++ openair3/NAS/NR_UE/nr_nas_msg.c | 19 +++++++++++++++++++ openair3/NAS/NR_UE/nr_nas_msg.h | 2 ++ 3 files changed, 23 insertions(+) diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h index 4acf7368b5..b757620506 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h @@ -151,6 +151,8 @@ static const text_info_t sa_iei_s[] = {FOREACH_SA_IEI(TO_TEXT)}; typedef enum { FOREACH_CAUSE(TO_ENUM) } cause_id_t; +static const text_info_t fgmm_cause_s[] = {FOREACH_CAUSE(TO_TEXT)}; + /* 10.5.7.3 3GPP TS 24.008 */ typedef struct { uint8_t value; diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index b9b90ab339..941e0d7cdc 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -66,6 +66,7 @@ #include "openair2/SDAP/nr_sdap/nr_sdap.h" #include "fgs_nas_utils.h" #include "fgmm_service_accept.h" +#include "fgmm_service_reject.h" #define MAX_NAS_UE 4 @@ -1531,6 +1532,17 @@ static void handle_service_accept(nr_ue_nas_t *nas, const byte_array_t *buffer) print_info(msg.cause->cause, cause_text_info, sizeofArray(cause_text_info))); } +static void handle_service_reject(nr_ue_nas_t *nas, const byte_array_t *buffer) +{ + fgs_service_reject_msg_t msg = {0}; + decode_fgs_service_reject(&msg, buffer); + // Extract timer t3448 in seconds (optional IE) + nas->t3448 = process_gprs_timer(msg.t3448); + // Extract timer t3446 in seconds (optional IE) + nas->t3446 = process_gprs_timer(msg.t3446); + LOG_E(NAS, "Received NAS Service Reject message with cause %s\n", fgmm_cause_s[msg.cause].text); +} + void *nas_nrue(void *args_p) { // Wait for a message or an event @@ -1737,6 +1749,13 @@ void *nas_nrue(void *args_p) handle_service_accept(nas, &buffer); break; } + + case FGS_SERVICE_REJECT: { + byte_array_t buffer = {.buf = pdu_buffer, .len = pdu_length}; + handle_service_reject(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 3bba6b1cec..c4f1d04e28 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.h +++ b/openair3/NAS/NR_UE/nr_nas_msg.h @@ -100,6 +100,8 @@ typedef struct { int t3512; // Timer t3448 in seconds (-1 = disabled) int t3448; + // Timer t3446 in seconds (-1 = disabled) + int t3446; } nr_ue_nas_t; nr_ue_nas_t *get_ue_nas_info(module_id_t module_id);