diff --git a/configs/meson.build b/configs/meson.build
index 91c073b0a..7994be677 100644
--- a/configs/meson.build
+++ b/configs/meson.build
@@ -33,6 +33,7 @@ conf_data.set('build_subprojects_freeDiameter_extensions_dir',
example_conf = '''
sample.yaml
eir.yaml
+ s13.yaml
attach.yaml
dns.yaml
310014.yaml
diff --git a/configs/s13.yaml.in b/configs/s13.yaml.in
new file mode 100644
index 000000000..b3cb72437
--- /dev/null
+++ b/configs/s13.yaml.in
@@ -0,0 +1,277 @@
+# Dedicated configuration for tests/s13: EPS attach with the ME Identity
+# Check over S13 served by open5gs-eird. The 5GC network functions are not
+# started, except the NRF that the EIR registers its SBI service with.
+db_uri: mongodb://localhost/open5gs
+
+logger:
+
+test:
+ serving:
+ - plmn_id:
+ mcc: 999
+ mnc: 70
+
+global:
+ parameter:
+ no_scp: true
+ no_sepp: true
+ no_amf: true
+ no_ausf: true
+ no_udm: true
+ no_pcf: true
+ no_nssf: true
+ no_bsf: true
+ no_udr: true
+
+mme:
+ freeDiameter:
+ identity: mme.localdomain
+ realm: localdomain
+ listen_on: 127.0.0.2
+ no_fwd: true
+ load_extension:
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dbg_msg_dumps.fdx
+ conf: 0x8888
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_rfc5777.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_mip6i.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nasreq.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nas_mipv6.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca_3gpp/dict_dcca_3gpp.fdx
+ connect:
+ - identity: hss.localdomain
+ address: 127.0.0.8
+ - identity: eir.localdomain
+ address: 127.0.0.21
+ eir:
+ enabled: true
+ realm: localdomain
+ host: eir.localdomain
+ timeout: 3
+ max_age: 1 # every test case shares one IMEISV: let verdicts expire
+ unknown_action: allow
+ failure_action: allow
+ missing_pei_action: allow
+
+ s1ap:
+ server:
+ - address: 127.0.0.2
+ gtpc:
+ server:
+ - address: 127.0.0.2
+ client:
+ sgwc:
+ - address: 127.0.0.3
+ smf:
+ - address: 127.0.0.4
+ metrics:
+ server:
+ - address: 127.0.0.2
+ port: 9090
+ gummei:
+ - plmn_id:
+ mcc: 999
+ mnc: 70
+ mme_gid: 2
+ mme_code: 1
+ tai:
+ - plmn_id:
+ mcc: 999
+ mnc: 70
+ tac: 1
+ security:
+ integrity_order : [ EIA2, EIA1, EIA0 ]
+ ciphering_order : [ EEA0, EEA1, EEA2 ]
+ network_name:
+ full: Open5GS
+ time:
+ t3412:
+ value: 540
+
+sgwc:
+ gtpc:
+ server:
+ - address: 127.0.0.3
+ pfcp:
+ server:
+ - address: 127.0.0.3
+ client:
+ sgwu:
+ - address: 127.0.0.6
+
+smf:
+ pfcp:
+ server:
+ - address: 127.0.0.4
+ client:
+ upf:
+ - address: 127.0.0.7
+ gtpc:
+ server:
+ - address: 127.0.0.4
+ gtpu:
+ server:
+ - address: 127.0.0.4
+ metrics:
+ server:
+ - address: 127.0.0.4
+ port: 9090
+ session:
+ - subnet: 10.45.0.0/16
+ gateway: 10.45.0.1
+ - subnet: 2001:db8:cafe::/48
+ gateway: 2001:db8:cafe::1
+ dns:
+ - 8.8.8.8
+ - 8.8.4.4
+ - 2001:4860:4860::8888
+ - 2001:4860:4860::8844
+ mtu: 1400
+ freeDiameter:
+ identity: smf.localdomain
+ realm: localdomain
+ listen_on: 127.0.0.4
+ no_fwd: true
+ load_extension:
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dbg_msg_dumps.fdx
+ conf: 0x8888
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_rfc5777.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_mip6i.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nasreq.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nas_mipv6.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca_3gpp/dict_dcca_3gpp.fdx
+ connect:
+ - identity: pcrf.localdomain
+ address: 127.0.0.9
+
+amf:
+ sbi:
+ server:
+ - address: 127.0.0.5
+ port: 7777
+ client:
+ nrf:
+ - uri: http://127.0.0.10:7777
+ ngap:
+ server:
+ - address: 127.0.0.5
+ guami:
+ - plmn_id:
+ mcc: 999
+ mnc: 70
+ amf_id:
+ region: 2
+ set: 1
+ tai:
+ - plmn_id:
+ mcc: 999
+ mnc: 70
+ tac: 1
+ plmn_support:
+ - plmn_id:
+ mcc: 999
+ mnc: 70
+ s_nssai:
+ - sst: 1
+ security:
+ integrity_order : [ NIA2, NIA1, NIA0 ]
+ ciphering_order : [ NEA0, NEA1, NEA2 ]
+ network_name:
+ full: Open5GS
+ amf_name: open5gs-amf0
+
+sgwu:
+ pfcp:
+ server:
+ - address: 127.0.0.6
+ gtpu:
+ server:
+ - address: 127.0.0.6
+
+upf:
+ pfcp:
+ server:
+ - address: 127.0.0.7
+ gtpu:
+ server:
+ - address: 127.0.0.7
+ session:
+ - subnet: 10.45.0.0/16
+ gateway: 10.45.0.1
+ - subnet: 2001:db8:cafe::/48
+ gateway: 2001:db8:cafe::1
+ metrics:
+ server:
+ - address: 127.0.0.7
+ port: 9090
+
+hss:
+ freeDiameter:
+ identity: hss.localdomain
+ realm: localdomain
+ listen_on: 127.0.0.8
+ no_fwd: true
+ load_extension:
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dbg_msg_dumps.fdx
+ conf: 0x8888
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_rfc5777.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_mip6i.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nasreq.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nas_mipv6.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca_3gpp/dict_dcca_3gpp.fdx
+ connect:
+ - identity: mme.localdomain
+ address: 127.0.0.2
+
+pcrf:
+ freeDiameter:
+ identity: pcrf.localdomain
+ realm: localdomain
+ listen_on: 127.0.0.9
+ no_fwd: true
+ load_extension:
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dbg_msg_dumps.fdx
+ conf: 0x8888
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_rfc5777.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_mip6i.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nasreq.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nas_mipv6.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca_3gpp/dict_dcca_3gpp.fdx
+ connect:
+ - identity: smf.localdomain
+ address: 127.0.0.4
+
+nrf:
+ sbi:
+ server:
+ - address: 127.0.0.10
+ port: 7777
+
+eir:
+ sbi:
+ server:
+ - address: 127.0.0.21
+ port: 7777
+ client:
+ nrf:
+ - uri: http://127.0.0.10:7777
+ freeDiameter:
+ identity: eir.localdomain
+ realm: localdomain
+ listen_on: 127.0.0.21
+ no_fwd: true
+ load_extension:
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dbg_msg_dumps.fdx
+ conf: 0x8888
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_rfc5777.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_mip6i.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nasreq.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nas_mipv6.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca.fdx
+ - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca_3gpp/dict_dcca_3gpp.fdx
+ connect:
+ - identity: mme.localdomain
+ address: 127.0.0.2
diff --git a/tests/app/app-init.c b/tests/app/app-init.c
index 0139c5c29..8cc6006c6 100644
--- a/tests/app/app-init.c
+++ b/tests/app/app-init.c
@@ -24,6 +24,7 @@ static ogs_thread_t *scp_thread = NULL;
static ogs_thread_t *sepp_thread = NULL;
static ogs_thread_t *pcrf_thread = NULL;
static ogs_thread_t *hss_thread = NULL;
+static ogs_thread_t *eir_thread = NULL;
static ogs_thread_t *upf_thread = NULL;
static ogs_thread_t *sgwc_thread = NULL;
static ogs_thread_t *sgwu_thread = NULL;
@@ -65,6 +66,9 @@ int app_initialize(const char *const argv[])
sepp_thread = test_child_create("sepp", 0, argv_out);
if (ogs_global_conf()->parameter.no_hss == 0)
hss_thread = test_child_create("hss", 0, argv_out);
+ if (ogs_global_conf()->parameter.no_eir == 0 &&
+ ogs_global_conf()->parameter.eir_count > 0)
+ eir_thread = test_child_create("eir", 0, argv_out);
if (ogs_global_conf()->parameter.no_pcrf == 0)
pcrf_thread = test_child_create("pcrf", 0, argv_out);
@@ -124,6 +128,7 @@ void app_terminate(void)
if (udm_thread) ogs_thread_destroy(udm_thread);
if (ausf_thread) ogs_thread_destroy(ausf_thread);
+ if (eir_thread) ogs_thread_destroy(eir_thread);
if (hss_thread) ogs_thread_destroy(hss_thread);
if (pcrf_thread) ogs_thread_destroy(pcrf_thread);
diff --git a/tests/app/epc-init.c b/tests/app/epc-init.c
index 00a0ced47..2d622e98e 100644
--- a/tests/app/epc-init.c
+++ b/tests/app/epc-init.c
@@ -25,6 +25,7 @@ static ogs_thread_t *smf_thread = NULL;
static ogs_thread_t *sgwc_thread = NULL;
static ogs_thread_t *sgwu_thread = NULL;
static ogs_thread_t *hss_thread = NULL;
+static ogs_thread_t *eir_thread = NULL;
static ogs_thread_t *mme_thread = NULL;
int app_initialize(const char *const argv[])
@@ -49,6 +50,9 @@ int app_initialize(const char *const argv[])
if (ogs_global_conf()->parameter.no_hss == 0)
hss_thread = test_child_create("hss", 0, argv_out);
+ if (ogs_global_conf()->parameter.no_eir == 0 &&
+ ogs_global_conf()->parameter.eir_count > 0)
+ eir_thread = test_child_create("eir", 0, argv_out);
if (ogs_global_conf()->parameter.no_pcrf == 0)
pcrf_thread = test_child_create("pcrf", 0, argv_out);
@@ -85,6 +89,7 @@ void app_terminate(void)
if (sgwu_thread) ogs_thread_destroy(sgwu_thread);
if (upf_thread) ogs_thread_destroy(upf_thread);
+ if (eir_thread) ogs_thread_destroy(eir_thread);
if (hss_thread) ogs_thread_destroy(hss_thread);
if (pcrf_thread) ogs_thread_destroy(pcrf_thread);
}
diff --git a/tests/common/context.h b/tests/common/context.h
index 9296bb41f..53ddf81ba 100644
--- a/tests/common/context.h
+++ b/tests/common/context.h
@@ -411,6 +411,7 @@ typedef struct test_ue_s {
/* EPC: Last received message */
S1AP_ProcedureCode_t s1ap_procedure_code;
uint8_t emm_message_type;
+ ogs_nas_emm_cause_t attach_reject_cause;
uint8_t esm_message_type;
test_sess_t *sess;
diff --git a/tests/common/nas-path.c b/tests/common/nas-path.c
index 0e5c48f56..db8143dad 100644
--- a/tests/common/nas-path.c
+++ b/tests/common/nas-path.c
@@ -160,6 +160,8 @@ void testemm_recv(test_ue_t *test_ue, ogs_pkbuf_t *pkbuf)
testemm_handle_attach_accept(test_ue, &message.emm.attach_accept);
break;
case OGS_NAS_EPS_ATTACH_REJECT:
+ test_ue->attach_reject_cause =
+ message.emm.attach_reject.emm_cause;
break;
case OGS_NAS_EPS_SERVICE_REJECT:
break;
diff --git a/tests/meson.build b/tests/meson.build
index e3f14a1b6..7e0f1023e 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -25,6 +25,7 @@ subdir('af')
subdir('common')
subdir('app')
subdir('eir')
+subdir('s13')
subdir('registration')
subdir('vonr')
subdir('slice')
diff --git a/tests/s13/abts-main.c b/tests/s13/abts-main.c
new file mode 100644
index 000000000..3d40fcc48
--- /dev/null
+++ b/tests/s13/abts-main.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2026 by LetMeConnect
+ *
+ * This file is part of Open5GS.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "test-app.h"
+
+abts_suite *test_s13(abts_suite *suite);
+
+const struct testlist {
+ abts_suite *(*func)(abts_suite *suite);
+} alltests[] = {
+ {test_s13},
+ {NULL},
+};
+
+static void terminate(void)
+{
+ ogs_msleep(50);
+
+ test_child_terminate();
+ app_terminate();
+
+ test_app_final();
+ ogs_app_terminate();
+}
+
+static void initialize(const char *const argv[])
+{
+ int rv;
+
+ rv = ogs_app_initialize(NULL, NULL, argv);
+ ogs_assert(rv == OGS_OK);
+ test_app_init();
+
+ rv = app_initialize(argv);
+ ogs_assert(rv == OGS_OK);
+}
+
+int main(int argc, const char *const argv[])
+{
+ int i;
+ abts_suite *suite = NULL;
+
+ atexit(terminate);
+ test_app_run(argc, argv, "s13.yaml", initialize);
+
+ for (i = 0; alltests[i].func; i++)
+ suite = alltests[i].func(suite);
+
+ return abts_report(suite);
+}
diff --git a/tests/s13/meson.build b/tests/s13/meson.build
new file mode 100644
index 000000000..51ce728c3
--- /dev/null
+++ b/tests/s13/meson.build
@@ -0,0 +1,14 @@
+# Copyright (C) 2026 by LetMeConnect
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+testapp_s13_sources = files('''
+ abts-main.c
+ s13-test.c
+'''.split())
+
+testapp_s13_exe = executable('s13',
+ sources : testapp_s13_sources,
+ c_args : testunit_core_cc_flags,
+ dependencies : libtestapp_dep)
+
+test('s13', testapp_s13_exe, is_parallel : false, suite: ['s13', 'epc'])
diff --git a/tests/s13/s13-test.c b/tests/s13/s13-test.c
new file mode 100644
index 000000000..23f7f43b7
--- /dev/null
+++ b/tests/s13/s13-test.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2026 by LetMeConnect
+ *
+ * This file is part of Open5GS.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/*
+ * ME Identity Check over S13 against the Open5GS EIR (open5gs-eird).
+ *
+ * The MME runs with eir.enabled (configs/s13.yaml) and the EIR serves S13
+ * next to N5g-eir out of the same eir collection. Each case provisions
+ * records for the test UE IMEISV, runs an EPS attach, and checks what
+ * follows Security Mode Complete: ESM Information Request when the
+ * equipment is admitted, Attach Reject #6 Illegal ME when it is not.
+ */
+
+#include "test-app.h"
+
+/* test_ue_set_mobile_identity_imsisv(): IMEI 86650704004053 + SVN 01 */
+#define S13_TEST_PEI "imeisv-8665070400405301"
+#define S13_TEST_OTHER_SUPI "imsi-999700000000099"
+
+typedef struct s13_case_s {
+ const char *name;
+ const char *generic_status; /* eir record for the PEI only */
+ const char *specific_status; /* eir record for the PEI and a SUPI */
+ bool specific_other_supi; /* ...of another subscriber */
+ ogs_nas_emm_cause_t reject_cause; /* 0: attach accepted */
+} s13_case_t;
+
+static const s13_case_t cases[] = {
+ { "whitelisted equipment attaches", "WHITELISTED", NULL, false, 0 },
+ { "greylisted equipment attaches", "GREYLISTED", NULL, false, 0 },
+ { "blacklisted equipment is rejected", "BLACKLISTED", NULL, false,
+ OGS_NAS_EMM_CAUSE_ILLEGAL_ME },
+ { "unknown equipment attaches with unknown_action allow",
+ NULL, NULL, false, 0 },
+ { "subscriber blacklist overrides generic whitelist",
+ "WHITELISTED", "BLACKLISTED", false, OGS_NAS_EMM_CAUSE_ILLEGAL_ME },
+ { "subscriber whitelist overrides generic blacklist",
+ "BLACKLISTED", "WHITELISTED", false, 0 },
+ { "record of another subscriber does not apply",
+ "WHITELISTED", "BLACKLISTED", true, 0 },
+};
+
+typedef struct fixture_s {
+ bson_oid_t ids[2];
+ unsigned int count;
+} fixture_t;
+
+static void fixture_insert(abts_case *tc, fixture_t *fixture,
+ const char *pei, const char *supi, const char *status)
+{
+ bson_error_t error;
+ bson_t *document = NULL;
+ unsigned int index = fixture->count++;
+
+ ogs_assert(index < OGS_ARRAY_SIZE(fixture->ids));
+ bson_oid_init(&fixture->ids[index], NULL);
+
+ if (supi)
+ document = BCON_NEW("_id", BCON_OID(&fixture->ids[index]),
+ "pei", BCON_UTF8(pei), "supi", BCON_UTF8(supi),
+ "status", BCON_UTF8(status));
+ else
+ document = BCON_NEW("_id", BCON_OID(&fixture->ids[index]),
+ "pei", BCON_UTF8(pei), "status", BCON_UTF8(status));
+ ogs_assert(document);
+
+ if (!mongoc_collection_insert_one(ogs_mongoc()->collection.eir,
+ document, NULL, NULL, &error))
+ ABTS_FAIL(tc, error.message);
+ bson_destroy(document);
+}
+
+static void fixture_remove(abts_case *tc, fixture_t *fixture)
+{
+ bson_error_t error;
+
+ /* Remove only documents created by this case */
+ while (fixture->count) {
+ bson_t *query = BCON_NEW("_id",
+ BCON_OID(&fixture->ids[--fixture->count]));
+
+ if (!mongoc_collection_delete_one(ogs_mongoc()->collection.eir,
+ query, NULL, NULL, &error))
+ ABTS_FAIL(tc, error.message);
+ bson_destroy(query);
+ }
+}
+
+static void s13_case(abts_case *tc, void *data)
+{
+ const s13_case_t *test = data;
+ int rv;
+ ogs_socknode_t *s1ap;
+ ogs_socknode_t *gtpu;
+ ogs_pkbuf_t *emmbuf;
+ ogs_pkbuf_t *esmbuf;
+ ogs_pkbuf_t *sendbuf;
+ ogs_pkbuf_t *recvbuf;
+
+ ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci;
+ test_ue_t *test_ue = NULL;
+ test_sess_t *sess = NULL;
+ test_bearer_t *bearer = NULL;
+
+ uint32_t enb_ue_s1ap_id;
+
+ bson_t *doc = NULL;
+ fixture_t fixture;
+ char *supi = NULL;
+
+ ogs_info("[S13] %s", test->name);
+ memset(&fixture, 0, sizeof(fixture));
+
+ /*
+ * Every case uses the same IMEISV and mme.eir.max_age is 1 second in
+ * s13.yaml: let the previous verdict expire from the MME cache so that
+ * the ME-Identity-Check-Request really reaches the EIR.
+ */
+ ogs_msleep(1200);
+
+ /* Setup Test UE & Session Context */
+ memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci));
+
+ mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI;
+ mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI;
+ mobile_identity_suci.routing_indicator1 = 0;
+ mobile_identity_suci.routing_indicator2 = 0xf;
+ mobile_identity_suci.routing_indicator3 = 0xf;
+ mobile_identity_suci.routing_indicator4 = 0xf;
+ mobile_identity_suci.protection_scheme_id = OGS_PROTECTION_SCHEME_NULL;
+ mobile_identity_suci.home_network_pki_value = 0;
+
+ test_ue = test_ue_add_by_suci(&mobile_identity_suci, "3746000006");
+ ogs_assert(test_ue);
+
+ test_ue->e_cgi.cell_id = 0x1079baf0;
+ test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE;
+ test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH;
+
+ test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc";
+ test_ue->opc_string = "e8ed289deba952e4283b54e88e6183ca";
+
+ sess = test_sess_add_by_apn(test_ue, "internet", OGS_GTP2_RAT_TYPE_EUTRAN);
+ ogs_assert(sess);
+
+ supi = ogs_msprintf("%s-%s", OGS_ID_SUPI_TYPE_IMSI, test_ue->imsi);
+ ogs_assert(supi);
+
+ /* eNB connects to MME */
+ s1ap = tests1ap_client(AF_INET);
+ ABTS_PTR_NOTNULL(tc, s1ap);
+
+ /* eNB connects to SGW */
+ gtpu = test_gtpu_server(1, AF_INET);
+ ABTS_PTR_NOTNULL(tc, gtpu);
+
+ /* Send S1-Setup Reqeust */
+ sendbuf = test_s1ap_build_s1_setup_request(
+ S1AP_ENB_ID_PR_macroENB_ID, 0x54f64);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ /* Receive S1-Setup Response */
+ recvbuf = testenb_s1ap_read(s1ap);
+ ABTS_PTR_NOTNULL(tc, recvbuf);
+ tests1ap_recv(NULL, recvbuf);
+
+ /********** Insert Subscriber in Database */
+ doc = test_db_new_simple(test_ue);
+ ABTS_PTR_NOTNULL(tc, doc);
+ ABTS_INT_EQUAL(tc, OGS_OK, test_db_insert_ue(test_ue, doc));
+
+ /********** Insert equipment records in the eir collection */
+ if (test->generic_status)
+ fixture_insert(tc, &fixture, S13_TEST_PEI, NULL, test->generic_status);
+ if (test->specific_status)
+ fixture_insert(tc, &fixture, S13_TEST_PEI,
+ test->specific_other_supi ? S13_TEST_OTHER_SUPI : supi,
+ test->specific_status);
+
+ /* Send Attach Request */
+ memset(&sess->pdn_connectivity_param,
+ 0, sizeof(sess->pdn_connectivity_param));
+ sess->pdn_connectivity_param.eit = 1;
+ sess->pdn_connectivity_param.request_type =
+ OGS_NAS_EPS_REQUEST_TYPE_INITIAL;
+ esmbuf = testesm_build_pdn_connectivity_request(
+ sess, false, OGS_NAS_EPS_PDN_TYPE_IPV4V6);
+ ABTS_PTR_NOTNULL(tc, esmbuf);
+
+ memset(&test_ue->attach_request_param,
+ 0, sizeof(test_ue->attach_request_param));
+ test_ue->attach_request_param.drx_parameter = 1;
+ test_ue->attach_request_param.ms_network_capability = 1;
+ test_ue->attach_request_param.tmsi_status = 1;
+ test_ue->attach_request_param.mobile_station_classmark_2 = 1;
+ test_ue->attach_request_param.ue_usage_setting = 1;
+ emmbuf = testemm_build_attach_request(test_ue, esmbuf, true, false);
+ ABTS_PTR_NOTNULL(tc, emmbuf);
+
+ memset(&test_ue->initial_ue_param, 0, sizeof(test_ue->initial_ue_param));
+ sendbuf = test_s1ap_build_initial_ue_message(
+ test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Signalling, false);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ /* Receive Authentication Request */
+ recvbuf = testenb_s1ap_read(s1ap);
+ ABTS_PTR_NOTNULL(tc, recvbuf);
+ tests1ap_recv(test_ue, recvbuf);
+
+ /* Send Authentication response */
+ emmbuf = testemm_build_authentication_response(test_ue);
+ ABTS_PTR_NOTNULL(tc, emmbuf);
+ sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ /* Receive Security mode Command */
+ recvbuf = testenb_s1ap_read(s1ap);
+ ABTS_PTR_NOTNULL(tc, recvbuf);
+ tests1ap_recv(test_ue, recvbuf);
+
+ /* Send Security mode complete, with the IMEISV the MME checks */
+ test_ue->mobile_identity_imeisv_presence = true;
+ emmbuf = testemm_build_security_mode_complete(test_ue);
+ ABTS_PTR_NOTNULL(tc, emmbuf);
+ sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ if (test->reject_cause) {
+ /* Receive Attach Reject */
+ test_ue->emm_message_type = 0;
+ test_ue->attach_reject_cause = 0;
+ recvbuf = testenb_s1ap_read(s1ap);
+ ABTS_PTR_NOTNULL(tc, recvbuf);
+ tests1ap_recv(test_ue, recvbuf);
+ ABTS_INT_EQUAL(tc, OGS_NAS_EPS_ATTACH_REJECT,
+ test_ue->emm_message_type);
+ ABTS_INT_EQUAL(tc, test->reject_cause, test_ue->attach_reject_cause);
+
+ /* Receive UE Context Release Command */
+ recvbuf = testenb_s1ap_read(s1ap);
+ ABTS_PTR_NOTNULL(tc, recvbuf);
+ tests1ap_recv(test_ue, recvbuf);
+ ABTS_INT_EQUAL(tc, S1AP_ProcedureCode_id_UEContextRelease,
+ test_ue->s1ap_procedure_code);
+
+ /* Send UE Context Release Complete */
+ sendbuf = test_s1ap_build_ue_context_release_complete(test_ue);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ goto cleanup;
+ }
+
+ /* Receive ESM Information Request */
+ test_ue->esm_message_type = 0;
+ recvbuf = testenb_s1ap_read(s1ap);
+ ABTS_PTR_NOTNULL(tc, recvbuf);
+ tests1ap_recv(test_ue, recvbuf);
+ ABTS_INT_EQUAL(tc, OGS_NAS_EPS_ESM_INFORMATION_REQUEST,
+ test_ue->esm_message_type);
+
+ /* Send ESM Information Response */
+ sess->esm_information_param.epco = 1;
+ esmbuf = testesm_build_esm_information_response(sess);
+ ABTS_PTR_NOTNULL(tc, esmbuf);
+ sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, esmbuf);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ /* Receive Initial Context Setup Request +
+ * Attach Accept +
+ * Activate Default Bearer Context Request */
+ recvbuf = testenb_s1ap_read(s1ap);
+ ABTS_PTR_NOTNULL(tc, recvbuf);
+ tests1ap_recv(test_ue, recvbuf);
+ ABTS_INT_EQUAL(tc, OGS_NAS_EPS_ATTACH_ACCEPT, test_ue->emm_message_type);
+
+ /* Send UE Capability Info Indication */
+ sendbuf = tests1ap_build_ue_radio_capability_info_indication(test_ue);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ /* Send Initial Context Setup Response */
+ sendbuf = test_s1ap_build_initial_context_setup_response(test_ue);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ /* Send Attach Complete + Activate default EPS bearer cotext accept */
+ test_ue->nr_cgi.cell_id = 0x1234502;
+ bearer = test_bearer_find_by_ue_ebi(test_ue, 5);
+ ogs_assert(bearer);
+ esmbuf = testesm_build_activate_default_eps_bearer_context_accept(
+ bearer, false);
+ ABTS_PTR_NOTNULL(tc, esmbuf);
+ emmbuf = testemm_build_attach_complete(test_ue, esmbuf);
+ ABTS_PTR_NOTNULL(tc, emmbuf);
+ sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ /* Receive EMM information */
+ recvbuf = testenb_s1ap_read(s1ap);
+ ABTS_PTR_NOTNULL(tc, recvbuf);
+ tests1ap_recv(test_ue, recvbuf);
+
+ /* Send Detach Request */
+ emmbuf = testemm_build_detach_request(test_ue, 1, true, false);
+ ABTS_PTR_NOTNULL(tc, emmbuf);
+ sendbuf = test_s1ap_build_initial_ue_message(
+ test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Signalling, true);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ /* Receive OLD UE Context Release Command */
+ enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id;
+
+ recvbuf = testenb_s1ap_read(s1ap);
+ ABTS_PTR_NOTNULL(tc, recvbuf);
+ tests1ap_recv(test_ue, recvbuf);
+
+ /* Send OLD UE Context Release Complete */
+ sendbuf = test_s1ap_build_ue_context_release_complete(test_ue);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+ test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id;
+
+ /* Receive UE Context Release Command */
+ recvbuf = testenb_s1ap_read(s1ap);
+ ABTS_PTR_NOTNULL(tc, recvbuf);
+ tests1ap_recv(test_ue, recvbuf);
+
+ /* Send UE Context Release Complete */
+ sendbuf = test_s1ap_build_ue_context_release_complete(test_ue);
+ ABTS_PTR_NOTNULL(tc, sendbuf);
+ rv = testenb_s1ap_send(s1ap, sendbuf);
+ ABTS_INT_EQUAL(tc, OGS_OK, rv);
+
+cleanup:
+ ogs_msleep(300);
+
+ /********** Remove equipment records and Subscriber in Database */
+ fixture_remove(tc, &fixture);
+ ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue));
+
+ /* eNB disonncect from MME */
+ testenb_s1ap_close(s1ap);
+
+ /* eNB disonncect from SGW */
+ test_gtpu_close(gtpu);
+
+ ogs_free(supi);
+ test_ue_remove(test_ue);
+}
+
+abts_suite *test_s13(abts_suite *suite)
+{
+ int i;
+
+ suite = ADD_SUITE(suite)
+
+ for (i = 0; i < OGS_ARRAY_SIZE(cases); i++)
+ abts_run_test(suite, s13_case, (void *)&cases[i]);
+
+ return suite;
+}