From a3a683e5a623b0553add0a65e3686c8e5d550fd3 Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Sat, 16 Sep 2023 20:58:10 +0900 Subject: [PATCH] [MME] Implement ENBConfigurationUpdate (#2589) --- src/mme/emm-handler.c | 9 ++- src/mme/s1ap-build.c | 82 ++++++++++++++++++++++++ src/mme/s1ap-build.h | 4 ++ src/mme/s1ap-handler.c | 123 ++++++++++++++++++++++++++++++++++++ src/mme/s1ap-handler.h | 2 + src/mme/s1ap-path.c | 50 +++++++++++++++ src/mme/s1ap-path.h | 4 ++ src/mme/s1ap-sm.c | 3 + tests/attach/s1setup-test.c | 48 ++++++++++++++ tests/common/s1ap-build.c | 42 ++++++++++++ tests/common/s1ap-build.h | 1 + 11 files changed, 366 insertions(+), 2 deletions(-) diff --git a/src/mme/emm-handler.c b/src/mme/emm-handler.c index 78d897bd9..20ae5c990 100644 --- a/src/mme/emm-handler.c +++ b/src/mme/emm-handler.c @@ -217,8 +217,13 @@ int emm_handle_attach_request(mme_ue_t *mme_ue, switch (eps_mobile_identity->imsi.type) { case OGS_NAS_EPS_MOBILE_IDENTITY_IMSI: - ogs_assert(sizeof(ogs_nas_mobile_identity_imsi_t) == - eps_mobile_identity->length); + if (sizeof(ogs_nas_mobile_identity_imsi_t) != + eps_mobile_identity->length) { + ogs_error("mobile_identity length (%d != %d)", + (int)sizeof(ogs_nas_mobile_identity_imsi_t), + eps_mobile_identity->length); + return OGS_ERROR; + } memcpy(&mme_ue->nas_mobile_identity_imsi, &eps_mobile_identity->imsi, eps_mobile_identity->length); ogs_nas_eps_imsi_to_bcd( diff --git a/src/mme/s1ap-build.c b/src/mme/s1ap-build.c index b880f4822..8a9eaae73 100644 --- a/src/mme/s1ap-build.c +++ b/src/mme/s1ap-build.c @@ -188,6 +188,88 @@ ogs_pkbuf_t *s1ap_build_setup_failure( return ogs_s1ap_encode(&pdu); } +ogs_pkbuf_t *s1ap_build_enb_configuration_update_ack(void) +{ + S1AP_S1AP_PDU_t pdu; + S1AP_SuccessfulOutcome_t *successfulOutcome = NULL; + + ogs_debug("ENBConfigurationUpdateAcknowledge"); + + memset(&pdu, 0, sizeof (S1AP_S1AP_PDU_t)); + pdu.present = S1AP_S1AP_PDU_PR_successfulOutcome; + pdu.choice.successfulOutcome = CALLOC(1, sizeof(S1AP_SuccessfulOutcome_t)); + + successfulOutcome = pdu.choice.successfulOutcome; + successfulOutcome->procedureCode = + S1AP_ProcedureCode_id_ENBConfigurationUpdate; + successfulOutcome->criticality = S1AP_Criticality_reject; + successfulOutcome->value.present = + S1AP_SuccessfulOutcome__value_PR_ENBConfigurationUpdateAcknowledge; + + return ogs_s1ap_encode(&pdu); +} + +ogs_pkbuf_t *s1ap_build_enb_configuration_update_failure( + S1AP_Cause_PR group, long cause, long time_to_wait) +{ + S1AP_S1AP_PDU_t pdu; + S1AP_UnsuccessfulOutcome_t *unsuccessfulOutcome = NULL; + S1AP_ENBConfigurationUpdateFailure_t *ENBConfigurationUpdateFailure = NULL; + + S1AP_ENBConfigurationUpdateFailureIEs_t *ie = NULL; + S1AP_Cause_t *Cause = NULL; + S1AP_TimeToWait_t *TimeToWait = NULL; + + ogs_debug("ENBConfigurationUpdateFailure"); + + ogs_debug(" Group[%d] Cause[%d] TimeToWait[%ld]", + group, (int)cause, time_to_wait); + + memset(&pdu, 0, sizeof (S1AP_S1AP_PDU_t)); + pdu.present = S1AP_S1AP_PDU_PR_unsuccessfulOutcome; + pdu.choice.unsuccessfulOutcome = + CALLOC(1, sizeof(S1AP_UnsuccessfulOutcome_t)); + + unsuccessfulOutcome = pdu.choice.unsuccessfulOutcome; + unsuccessfulOutcome->procedureCode = + S1AP_ProcedureCode_id_ENBConfigurationUpdate; + unsuccessfulOutcome->criticality = S1AP_Criticality_reject; + unsuccessfulOutcome->value.present = + S1AP_UnsuccessfulOutcome__value_PR_ENBConfigurationUpdateFailure; + + ENBConfigurationUpdateFailure = + &unsuccessfulOutcome->value.choice.ENBConfigurationUpdateFailure; + + ie = CALLOC(1, sizeof(S1AP_ENBConfigurationUpdateFailureIEs_t)); + ASN_SEQUENCE_ADD(&ENBConfigurationUpdateFailure->protocolIEs, ie); + + ie->id = S1AP_ProtocolIE_ID_id_Cause; + ie->criticality = S1AP_Criticality_ignore; + ie->value.present = S1AP_ENBConfigurationUpdateFailureIEs__value_PR_Cause; + + Cause = &ie->value.choice.Cause; + + if (time_to_wait > -1) { + ie = CALLOC(1, sizeof(S1AP_ENBConfigurationUpdateFailureIEs_t)); + ASN_SEQUENCE_ADD(&ENBConfigurationUpdateFailure->protocolIEs, ie); + + ie->id = S1AP_ProtocolIE_ID_id_TimeToWait; + ie->criticality = S1AP_Criticality_ignore; + ie->value.present = + S1AP_ENBConfigurationUpdateFailureIEs__value_PR_TimeToWait; + + TimeToWait = &ie->value.choice.TimeToWait; + } + + Cause->present = group; + Cause->choice.radioNetwork = cause; + + if (TimeToWait) + *TimeToWait = time_to_wait; + + return ogs_s1ap_encode(&pdu); +} + ogs_pkbuf_t *s1ap_build_downlink_nas_transport( enb_ue_t *enb_ue, ogs_pkbuf_t *emmbuf) { diff --git a/src/mme/s1ap-build.h b/src/mme/s1ap-build.h index 6c7423ff9..62ea967da 100644 --- a/src/mme/s1ap-build.h +++ b/src/mme/s1ap-build.h @@ -32,6 +32,10 @@ ogs_pkbuf_t *s1ap_build_setup_rsp(void); ogs_pkbuf_t *s1ap_build_setup_failure( S1AP_Cause_PR group, long cause, long time_to_wait); +ogs_pkbuf_t *s1ap_build_enb_configuration_update_ack(void); +ogs_pkbuf_t *s1ap_build_enb_configuration_update_failure( + S1AP_Cause_PR group, long cause, long time_to_wait); + ogs_pkbuf_t *s1ap_build_downlink_nas_transport( enb_ue_t *enb_ue, ogs_pkbuf_t *emmbuf); diff --git a/src/mme/s1ap-handler.c b/src/mme/s1ap-handler.c index 982825f88..f7bbbcca2 100644 --- a/src/mme/s1ap-handler.c +++ b/src/mme/s1ap-handler.c @@ -246,6 +246,129 @@ void s1ap_handle_s1_setup_request(mme_enb_t *enb, ogs_s1ap_message_t *message) ogs_assert(r != OGS_ERROR); } +void s1ap_handle_enb_configuration_update( + mme_enb_t *enb, ogs_s1ap_message_t *message) +{ + int i, j, r; + + S1AP_InitiatingMessage_t *initiatingMessage = NULL; + S1AP_ENBConfigurationUpdate_t *ENBConfigurationUpdate = NULL; + + S1AP_ENBConfigurationUpdateIEs_t *ie = NULL; + S1AP_SupportedTAs_t *SupportedTAs = NULL; + S1AP_PagingDRX_t *PagingDRX = NULL; + + ogs_assert(enb); + ogs_assert(enb->sctp.sock); + + ogs_assert(message); + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + ENBConfigurationUpdate = + &initiatingMessage->value.choice.ENBConfigurationUpdate; + ogs_assert(ENBConfigurationUpdate); + + ogs_debug("ENBConfigurationUpdate"); + + for (i = 0; i < ENBConfigurationUpdate->protocolIEs.list.count; i++) { + ie = ENBConfigurationUpdate->protocolIEs.list.array[i]; + switch (ie->id) { + case S1AP_ProtocolIE_ID_id_SupportedTAs: + SupportedTAs = &ie->value.choice.SupportedTAs; + break; + case S1AP_ProtocolIE_ID_id_DefaultPagingDRX: + PagingDRX = &ie->value.choice.PagingDRX; + break; + default: + break; + } + } + + /* Parse Supported TA */ + if (SupportedTAs) { + S1AP_Cause_PR group = S1AP_Cause_PR_NOTHING; + long cause = 0; + + enb->num_of_supported_ta_list = 0; + for (i = 0; i < SupportedTAs->list.count; i++) { + S1AP_SupportedTAs_Item_t *SupportedTAs_Item = NULL; + S1AP_TAC_t *tAC = NULL; + + SupportedTAs_Item = + (S1AP_SupportedTAs_Item_t *)SupportedTAs->list.array[i]; + ogs_assert(SupportedTAs_Item); + tAC = &SupportedTAs_Item->tAC; + ogs_assert(tAC); + + for (j = 0; j < SupportedTAs_Item->broadcastPLMNs.list.count; j++) { + S1AP_PLMNidentity_t *pLMNidentity = NULL; + pLMNidentity = (S1AP_PLMNidentity_t *) + SupportedTAs_Item->broadcastPLMNs.list.array[j]; + ogs_assert(pLMNidentity); + + memcpy(&enb->supported_ta_list[ + enb->num_of_supported_ta_list].tac, + tAC->buf, sizeof(uint16_t)); + enb->supported_ta_list[enb->num_of_supported_ta_list].tac = + be16toh(enb->supported_ta_list + [enb->num_of_supported_ta_list].tac); + memcpy(&enb->supported_ta_list + [enb->num_of_supported_ta_list].plmn_id, + pLMNidentity->buf, sizeof(ogs_plmn_id_t)); + ogs_debug(" PLMN_ID[MCC:%d MNC:%d] TAC[%d]", + ogs_plmn_id_mcc(&enb->supported_ta_list + [enb->num_of_supported_ta_list].plmn_id), + ogs_plmn_id_mnc(&enb->supported_ta_list + [enb->num_of_supported_ta_list].plmn_id), + enb->supported_ta_list[enb->num_of_supported_ta_list].tac); + enb->num_of_supported_ta_list++; + } + } + + /* + * TS36.413 + * Section 8.7.3.4 Abnormal Conditions + * + * If the eNB initiates the procedure by sending a S1 SETUP REQUEST + * message including the PLMN Identity IEs and none of the PLMNs + * provided by the eNB is identified by the MME, then the MME shall + * reject the eNB S1 Setup Request procedure with the appropriate cause + * value, e.g., “Unknown PLMN”. + */ + if (enb_plmn_id_is_foreign(enb)) { + ogs_warn("S1-Setup failure:"); + ogs_warn(" Global-ENB-ID PLMN-ID is foreign"); + group = S1AP_Cause_PR_misc; + cause = S1AP_CauseMisc_unknown_PLMN; + + r = s1ap_send_enb_configuration_update_failure(enb, group, cause); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + return; + } + + if (!served_tai_is_found(enb)) { + ogs_warn("S1-Setup failure:"); + ogs_warn(" Cannot find Served TAI. " + "Check 'mme.tai' configuration"); + group = S1AP_Cause_PR_misc; + cause = S1AP_CauseMisc_unknown_PLMN; + + r = s1ap_send_enb_configuration_update_failure(enb, group, cause); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + return; + } + } + + if (PagingDRX) + ogs_debug(" PagingDRX[%ld]", *PagingDRX); + + r = s1ap_send_enb_configuration_update_ack(enb); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); +} + void s1ap_handle_initial_ue_message(mme_enb_t *enb, ogs_s1ap_message_t *message) { int i, r; diff --git a/src/mme/s1ap-handler.h b/src/mme/s1ap-handler.h index 1d87cfdcc..7fab9b371 100644 --- a/src/mme/s1ap-handler.h +++ b/src/mme/s1ap-handler.h @@ -28,6 +28,8 @@ extern "C" { void s1ap_handle_s1_setup_request( mme_enb_t *enb, ogs_s1ap_message_t *message); +void s1ap_handle_enb_configuration_update( + mme_enb_t *enb, ogs_s1ap_message_t *message); void s1ap_handle_initial_ue_message( mme_enb_t *enb, ogs_s1ap_message_t *message); void s1ap_handle_uplink_nas_transport( diff --git a/src/mme/s1ap-path.c b/src/mme/s1ap-path.c index 6b3b71028..6ee5dcc61 100644 --- a/src/mme/s1ap-path.c +++ b/src/mme/s1ap-path.c @@ -309,6 +309,56 @@ int s1ap_send_s1_setup_failure( return rv; } +int s1ap_send_enb_configuration_update_ack(mme_enb_t *enb) +{ + int rv; + ogs_pkbuf_t *s1ap_buffer; + + ogs_debug("ENBConfigurationUpdateAcknowledge"); + + if (!mme_enb_cycle(enb)) { + ogs_error("eNB has already been removed"); + return OGS_NOTFOUND; + } + + s1ap_buffer = s1ap_build_enb_configuration_update_ack(); + if (!s1ap_buffer) { + ogs_error("s1ap_build_setup_rsp() failed"); + return OGS_ERROR; + } + + rv = s1ap_send_to_enb(enb, s1ap_buffer, S1AP_NON_UE_SIGNALLING); + ogs_expect(rv == OGS_OK); + + return rv; +} + +int s1ap_send_enb_configuration_update_failure( + mme_enb_t *enb, S1AP_Cause_PR group, long cause) +{ + int rv; + ogs_pkbuf_t *s1ap_buffer; + + ogs_debug("ENBConfigurationUpdateFailure"); + + if (!mme_enb_cycle(enb)) { + ogs_error("eNB has already been removed"); + return OGS_NOTFOUND; + } + + s1ap_buffer = s1ap_build_enb_configuration_update_failure( + group, cause, S1AP_TimeToWait_v10s); + if (!s1ap_buffer) { + ogs_error("s1ap_build_setup_failure() failed"); + return OGS_ERROR; + } + + rv = s1ap_send_to_enb(enb, s1ap_buffer, S1AP_NON_UE_SIGNALLING); + ogs_expect(rv == OGS_OK); + + return rv; +} + int s1ap_send_initial_context_setup_request(mme_ue_t *mme_ue) { int rv; diff --git a/src/mme/s1ap-path.h b/src/mme/s1ap-path.h index 8b1b65fe4..a23c52304 100644 --- a/src/mme/s1ap-path.h +++ b/src/mme/s1ap-path.h @@ -52,6 +52,10 @@ int s1ap_send_s1_setup_response(mme_enb_t *enb); int s1ap_send_s1_setup_failure( mme_enb_t *enb, S1AP_Cause_PR group, long cause); +int s1ap_send_enb_configuration_update_ack(mme_enb_t *enb); +int s1ap_send_enb_configuration_update_failure( + mme_enb_t *enb, S1AP_Cause_PR group, long cause); + int s1ap_send_initial_context_setup_request(mme_ue_t *mme_ue); int s1ap_send_ue_context_modification_request(mme_ue_t *mme_ue); int s1ap_send_ue_context_release_command( diff --git a/src/mme/s1ap-sm.c b/src/mme/s1ap-sm.c index 4ebf690f8..931431631 100644 --- a/src/mme/s1ap-sm.c +++ b/src/mme/s1ap-sm.c @@ -87,6 +87,9 @@ void s1ap_state_operational(ogs_fsm_t *s, mme_event_t *e) case S1AP_ProcedureCode_id_S1Setup : s1ap_handle_s1_setup_request(enb, pdu); break; + case S1AP_ProcedureCode_id_ENBConfigurationUpdate: + s1ap_handle_enb_configuration_update(enb, pdu); + break; case S1AP_ProcedureCode_id_initialUEMessage : s1ap_handle_initial_ue_message(enb, pdu); break; diff --git a/tests/attach/s1setup-test.c b/tests/attach/s1setup-test.c index e5e26ad2d..3e7606341 100644 --- a/tests/attach/s1setup-test.c +++ b/tests/attach/s1setup-test.c @@ -97,12 +97,60 @@ static void s1setup_test2(abts_case *tc, void *data) } } +static void s1setup_test3(abts_case *tc, void *data) +{ + int rv; + ogs_socknode_t *node[NUM_OF_TEST_ENB]; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + ogs_s1ap_message_t message; + int i; + + i = 0; + node[i] = tests1ap_client(AF_INET); + ABTS_PTR_NOTNULL(tc, node[i]); + + sendbuf = test_s1ap_build_s1_setup_request( + S1AP_ENB_ID_PR_macroENB_ID, 0x54f64+i); + ABTS_PTR_NOTNULL(tc, sendbuf); + + rv = testenb_s1ap_send(node[i], sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testenb_s1ap_read(node[i]); + ABTS_PTR_NOTNULL(tc, recvbuf); + + rv = ogs_s1ap_decode(&message, recvbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_s1ap_free(&message); + ogs_pkbuf_free(recvbuf); + + sendbuf = test_s1ap_build_enb_configuration_update(0); + ABTS_PTR_NOTNULL(tc, sendbuf); + + rv = testenb_s1ap_send(node[i], sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testenb_s1ap_read(node[i]); + ABTS_PTR_NOTNULL(tc, recvbuf); + + rv = ogs_s1ap_decode(&message, recvbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_s1ap_free(&message); + ogs_pkbuf_free(recvbuf); + + testenb_s1ap_close(node[i]); +} + abts_suite *test_s1setup(abts_suite *suite) { suite = ADD_SUITE(suite) abts_run_test(suite, s1setup_test1, NULL); abts_run_test(suite, s1setup_test2, NULL); + abts_run_test(suite, s1setup_test3, NULL); return suite; } diff --git a/tests/common/s1ap-build.c b/tests/common/s1ap-build.c index 1032eb5d7..be58b697b 100644 --- a/tests/common/s1ap-build.c +++ b/tests/common/s1ap-build.c @@ -2041,6 +2041,48 @@ ogs_pkbuf_t *test_s1ap_build_enb_configuration_transfer(int i) return pkbuf; } +ogs_pkbuf_t *test_s1ap_build_enb_configuration_update(int i) +{ + ogs_pkbuf_t *pkbuf = NULL; + const char *payload[TEST_S1AP_MAX_MESSAGE] = { + "001d002a0000" + "04003c4003000031 0040000700000040 34f2990089400140 0124400c00004ced" + "a80000004034f299", + "", + "", + + "", + "", + "", + + "", + "", + "", + + }; + uint16_t len[TEST_S1AP_MAX_MESSAGE] = { + 46, + 0, + 0, + + 0, + 0, + 0, + + 0, + 0, + 0, + }; + char hexbuf[OGS_HUGE_LEN]; + + pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); + ogs_assert(pkbuf); + ogs_pkbuf_put_data(pkbuf, + ogs_hex_from_string(payload[i], hexbuf, sizeof(hexbuf)), len[i]); + + return pkbuf; +} + ogs_pkbuf_t *test_s1ap_build_malformed_s1_setup_request(int i) { ogs_pkbuf_t *pkbuf = NULL; diff --git a/tests/common/s1ap-build.h b/tests/common/s1ap-build.h index 9060ec1b3..4e672aa13 100644 --- a/tests/common/s1ap-build.h +++ b/tests/common/s1ap-build.h @@ -67,6 +67,7 @@ ogs_pkbuf_t *test_s1ap_build_handover_failure(test_ue_t *test_ue, S1AP_Cause_PR group, long cause); ogs_pkbuf_t *test_s1ap_build_enb_configuration_transfer(int i); +ogs_pkbuf_t *test_s1ap_build_enb_configuration_update(int i); ogs_pkbuf_t *test_s1ap_build_malformed_s1_setup_request(int i); ogs_pkbuf_t *test_s1ap_build_malformed_enb_status_transfer(int i);