From df365d9a2dbd0ac31f7c389cdef4e95008323356 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 10 Feb 2025 15:41:44 +0100 Subject: [PATCH] Add F1 Reset Ack enc/dec/cp/eq/free library and test --- openair2/COMMON/f1ap_messages_types.h | 10 +- openair2/F1AP/lib/f1ap_interface_management.c | 115 ++++++++++++++++++ openair2/F1AP/lib/f1ap_interface_management.h | 7 ++ openair2/F1AP/tests/f1ap_lib_test.c | 45 +++++++ 4 files changed, 171 insertions(+), 6 deletions(-) diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index e72741f6ed..53740c1bde 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -578,12 +578,10 @@ typedef struct f1ap_reset_t { } f1ap_reset_t; typedef struct f1ap_reset_ack_t { - uint64_t transaction_id; - struct { - uint32_t gNB_CU_ue_id; - uint32_t gNB_DU_ue_id; - } ue_to_reset[F1AP_MAX_NO_OF_INDIVIDUAL_CONNECTIONS_TO_RESET]; - uint16_t criticality_diagnostics; + uint64_t transaction_id; + int num_ue_to_reset; + f1ap_ue_to_reset_t *ue_to_reset; // array of num_ue_to_reset elements + //uint16_t criticality_diagnostics; // not implemented as of now } f1ap_reset_ack_t; #endif /* F1AP_MESSAGES_TYPES_H_ */ diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c index ce7ece3c11..08f324a618 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.c +++ b/openair2/F1AP/lib/f1ap_interface_management.c @@ -252,6 +252,121 @@ f1ap_reset_t cp_f1ap_reset(const f1ap_reset_t *orig) return cp; } +/* @brief encode F1 Reset Ack (9.2.1.2 in TS 38.473) */ +struct F1AP_F1AP_PDU *encode_f1ap_reset_ack(const f1ap_reset_ack_t *msg) +{ + F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu)); + /* Create Message Type */ + pdu->present = F1AP_F1AP_PDU_PR_successfulOutcome; + asn1cCalloc(pdu->choice.successfulOutcome, so); + so->procedureCode = F1AP_ProcedureCode_id_Reset; + so->criticality = F1AP_Criticality_reject; + so->value.present = F1AP_SuccessfulOutcome__value_PR_ResetAcknowledge; + F1AP_ResetAcknowledge_t *reset_ack = &so->value.choice.ResetAcknowledge; + + /* (M) Transaction ID */ + asn1cSequenceAdd(reset_ack->protocolIEs.list, F1AP_ResetAcknowledgeIEs_t, ieC1); + ieC1->id = F1AP_ProtocolIE_ID_id_TransactionID; + ieC1->criticality = F1AP_Criticality_reject; + ieC1->value.present = F1AP_ResetAcknowledgeIEs__value_PR_TransactionID; + ieC1->value.choice.TransactionID = msg->transaction_id; + + /* TODO criticality diagnostics */ + + /* 0:N UEs to reset */ + if (msg->num_ue_to_reset == 0) + return pdu; /* no UEs to encode */ + + asn1cSequenceAdd(reset_ack->protocolIEs.list, F1AP_ResetAcknowledgeIEs_t, ieC2); + ieC2->id = F1AP_ProtocolIE_ID_id_UE_associatedLogicalF1_ConnectionListResAck; + ieC2->criticality = F1AP_Criticality_ignore; + ieC2->value.present = F1AP_ResetAcknowledgeIEs__value_PR_UE_associatedLogicalF1_ConnectionListResAck; + F1AP_UE_associatedLogicalF1_ConnectionListResAck_t *ue_to_reset = &ieC2->value.choice.UE_associatedLogicalF1_ConnectionListResAck; + for (int i = 0; i < msg->num_ue_to_reset; ++i) { + asn1cSequenceAdd(ue_to_reset->list, F1AP_UE_associatedLogicalF1_ConnectionItemResAck_t, conn_it_res); + conn_it_res->id = F1AP_ProtocolIE_ID_id_UE_associatedLogicalF1_ConnectionItem; + conn_it_res->criticality = F1AP_Criticality_ignore; + conn_it_res->value.present = F1AP_UE_associatedLogicalF1_ConnectionItemResAck__value_PR_UE_associatedLogicalF1_ConnectionItem; + conn_it_res->value.choice.UE_associatedLogicalF1_ConnectionItem = encode_f1ap_ue_to_reset(&msg->ue_to_reset[i]); + } + return pdu; +} + +/* @brief decode F1 Reset Ack (9.2.1.2 in TS 38.473) */ +bool decode_f1ap_reset_ack(const struct F1AP_F1AP_PDU *pdu, f1ap_reset_ack_t *out) +{ + _F1_EQ_CHECK_INT(pdu->present, F1AP_F1AP_PDU_PR_successfulOutcome); + AssertError(pdu->choice.successfulOutcome != NULL, return false, "pdu->choice.initiatingMessage is NULL"); + _F1_EQ_CHECK_LONG(pdu->choice.successfulOutcome->procedureCode, F1AP_ProcedureCode_id_Reset); + _F1_EQ_CHECK_INT(pdu->choice.successfulOutcome->value.present, F1AP_SuccessfulOutcome__value_PR_ResetAcknowledge); + + /* Check presence of mandatory IEs */ + F1AP_ResetAcknowledge_t *in = &pdu->choice.successfulOutcome->value.choice.ResetAcknowledge; + F1AP_ResetAcknowledgeIEs_t *ie; + F1AP_LIB_FIND_IE(F1AP_ResetAcknowledgeIEs_t, ie, in, F1AP_ProtocolIE_ID_id_TransactionID, true); + + /* Loop over all IEs */ + for (int i = 0; i < in->protocolIEs.list.count; i++) { + AssertError(in->protocolIEs.list.array[i] != NULL, return false, "in->protocolIEs.list.array[i] is NULL"); + ie = in->protocolIEs.list.array[i]; + switch (ie->id) { + case F1AP_ProtocolIE_ID_id_TransactionID: + // (M) Transaction ID + _F1_EQ_CHECK_INT(ie->value.present, F1AP_ResetAcknowledgeIEs__value_PR_TransactionID); + out->transaction_id = ie->value.choice.TransactionID; + break; + case F1AP_ProtocolIE_ID_id_UE_associatedLogicalF1_ConnectionListResAck: + _F1_EQ_CHECK_INT(ie->value.present, F1AP_ResetAcknowledgeIEs__value_PR_UE_associatedLogicalF1_ConnectionListResAck); + { + const F1AP_UE_associatedLogicalF1_ConnectionListResAck_t *conn_list = &ie->value.choice.UE_associatedLogicalF1_ConnectionListResAck; + AssertError(conn_list->list.count > 0, return false, "no UEs for partially reset F1 interface\n"); + out->num_ue_to_reset = conn_list->list.count; + out->ue_to_reset = calloc_or_fail(out->num_ue_to_reset, sizeof(*out->ue_to_reset)); + for (int i = 0; i < out->num_ue_to_reset; ++i) { + const F1AP_UE_associatedLogicalF1_ConnectionItemResAck_t *it_res = (const F1AP_UE_associatedLogicalF1_ConnectionItemResAck_t *)conn_list->list.array[i]; + _F1_EQ_CHECK_LONG(it_res->id, F1AP_ProtocolIE_ID_id_UE_associatedLogicalF1_ConnectionItem); + _F1_EQ_CHECK_INT(it_res->value.present, F1AP_UE_associatedLogicalF1_ConnectionItemResAck__value_PR_UE_associatedLogicalF1_ConnectionItem); + out->ue_to_reset[i] = decode_f1ap_ue_to_reset(&it_res->value.choice.UE_associatedLogicalF1_ConnectionItem); + } + } + break; + default: + AssertError(true, return false, "Reset Acknowledge: ProtocolIE id %ld not implemented, ignoring IE\n", ie->id); + break; + } + } + return true; +} + +void free_f1ap_reset_ack(f1ap_reset_ack_t *msg) +{ + for (int i = 0; i < msg->num_ue_to_reset; ++i) + free_f1ap_ue_to_reset(&msg->ue_to_reset[i]); + free(msg->ue_to_reset); +} + +bool eq_f1ap_reset_ack(const f1ap_reset_ack_t *a, const f1ap_reset_ack_t *b) +{ + _F1_EQ_CHECK_LONG(a->transaction_id, b->transaction_id); + _F1_EQ_CHECK_INT(a->num_ue_to_reset, b->num_ue_to_reset); + for (int i = 0; i < a->num_ue_to_reset; ++i) { + if (!eq_f1ap_ue_to_reset(&a->ue_to_reset[i], &b->ue_to_reset[i])) + return false; + } + return true; +} + +f1ap_reset_ack_t cp_f1ap_reset_ack(const f1ap_reset_ack_t *orig) +{ + f1ap_reset_ack_t cp = {.transaction_id = orig->transaction_id, .num_ue_to_reset = orig->num_ue_to_reset}; + if (cp.num_ue_to_reset > 0) { + cp.ue_to_reset = calloc_or_fail(cp.num_ue_to_reset, sizeof(*cp.ue_to_reset)); + for (int i = 0; i < cp.num_ue_to_reset; ++i) + cp.ue_to_reset[i] = cp_f1ap_ue_to_reset(&orig->ue_to_reset[i]); + } + return cp; +} + static const int nrb_lut[29] = {11, 18, 24, 25, 31, 32, 38, 51, 52, 65, 66, 78, 79, 93, 106, 107, 121, 132, 133, 135, 160, 162, 189, 216, 217, 245, 264, 270, 273}; diff --git a/openair2/F1AP/lib/f1ap_interface_management.h b/openair2/F1AP/lib/f1ap_interface_management.h index b3769eabfe..1b484f3476 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.h +++ b/openair2/F1AP/lib/f1ap_interface_management.h @@ -34,6 +34,13 @@ void free_f1ap_reset(f1ap_reset_t *msg); bool eq_f1ap_reset(const f1ap_reset_t *a, const f1ap_reset_t *b); f1ap_reset_t cp_f1ap_reset(const f1ap_reset_t *orig); +/* F1 Reset Ack */ +struct F1AP_F1AP_PDU *encode_f1ap_reset_ack(const f1ap_reset_ack_t *msg); +bool decode_f1ap_reset_ack(const struct F1AP_F1AP_PDU *pdu, f1ap_reset_ack_t *out); +void free_f1ap_reset_ack(f1ap_reset_ack_t *msg); +bool eq_f1ap_reset_ack(const f1ap_reset_ack_t *a, const f1ap_reset_ack_t *b); +f1ap_reset_ack_t cp_f1ap_reset_ack(const f1ap_reset_ack_t *orig); + struct F1AP_F1AP_PDU *encode_f1ap_setup_request(const f1ap_setup_req_t *msg); bool decode_f1ap_setup_request(const struct F1AP_F1AP_PDU *pdu, f1ap_setup_req_t *out); f1ap_setup_req_t cp_f1ap_setup_request(const f1ap_setup_req_t *msg); diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index 2e516a5607..29d4b625ed 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -447,6 +447,50 @@ static void test_f1ap_reset_part(void) free_f1ap_reset(&orig); } +/** + * @brief Test F1 reset ack with differently ack'd UEs ("generic ack" is + * special case with no individual UE acked) + */ +static void test_f1ap_reset_ack(void) +{ + f1ap_reset_ack_t orig = { + .transaction_id = 4, + .num_ue_to_reset = 4, + }; + orig.ue_to_reset = calloc_or_fail(orig.num_ue_to_reset, sizeof(*orig.ue_to_reset)); + orig.ue_to_reset[0].gNB_CU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[0].gNB_CU_ue_id)); + *orig.ue_to_reset[0].gNB_CU_ue_id = 10; + orig.ue_to_reset[1].gNB_DU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[1].gNB_DU_ue_id)); + *orig.ue_to_reset[1].gNB_DU_ue_id = 11; + orig.ue_to_reset[2].gNB_CU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[2].gNB_CU_ue_id)); + *orig.ue_to_reset[2].gNB_CU_ue_id = 12; + orig.ue_to_reset[2].gNB_DU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[2].gNB_DU_ue_id)); + *orig.ue_to_reset[2].gNB_DU_ue_id = 13; + // orig.ue_to_reset[3] intentionally empty (because it is allowed, both IDs are optional) + + F1AP_F1AP_PDU_t *f1enc = encode_f1ap_reset_ack(&orig); + F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); + f1ap_msg_free(f1enc); + + f1ap_reset_ack_t decoded = {0}; + bool ret = decode_f1ap_reset_ack(f1dec, &decoded); + AssertFatal(ret, "decode_f1ap_reset_ack(): could not decode message\n"); + f1ap_msg_free(f1dec); + + ret = eq_f1ap_reset_ack(&orig, &decoded); + AssertFatal(ret, "eq_f1ap_reset_ack(): decoded message doesn't match original\n"); + free_f1ap_reset_ack(&decoded); + + f1ap_reset_ack_t cp = cp_f1ap_reset_ack(&orig); + ret = eq_f1ap_reset_ack(&orig, &cp); + AssertFatal(ret, "eq_f1ap_reset_ack(): copied message doesn't match original\n"); + free_f1ap_reset_ack(&cp); + + printf("f1ap_reset_ack successful\n"); + + free_f1ap_reset_ack(&orig); +} + /** * @brief Test F1 gNB-DU Configuration Update */ @@ -671,6 +715,7 @@ int main() test_f1ap_setup_failure(); test_f1ap_reset_all(); test_f1ap_reset_part(); + test_f1ap_reset_ack(); test_f1ap_du_configuration_update(); test_f1ap_cu_configuration_update(); test_f1ap_cu_configuration_update_acknowledge();