forked from acouzens/open5gs
[MME] Implement ENBConfigurationUpdate (#2589)
This commit is contained in:
parent
aa746794e7
commit
a3a683e5a6
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue