fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/42359?usp=email )
Change subject: s1gw: add testcases for impatient eNB during MME pool selection ......................................................................
s1gw: add testcases for impatient eNB during MME pool selection
Two new test cases covering scenarios where the eNB disconnects before S1 setup completes, targeting specific states of the enb_proxy FSM:
* TC_mme_pool_enb_disc_wait_s1setup_req: eNB connects but disconnects before sending S1SetupReq (enb_proxy in wait_s1setup_req). No MME connection is ever attempted; S1GW must handle the disconnect cleanly.
* TC_mme_pool_enb_disc_wait_s1setup_rsp: eNB sends S1SetupReq, S1GW forwards it to the first pool MME (enb_proxy in wait_s1setup_rsp), then eNB disconnects before the response arrives. S1GW must detect the eNB disconnect and close the open MME connection in response.
A new helper S1GW_ConnHdlr.f_ConnHdlr_s1ap_close() is added for these tests: unlike f_ConnHdlr_s1ap_disconnect(), it closes the eNB-side socket without waiting for an S1APSRV_EVENT_CONN_DOWN from a pool server (since in these scenarios either no MME connection exists yet, or the CONN_DOWN is captured by the test body directly).
Change-Id: I5d27cdafcb9f595a2d3db59beff17cd55de2539e Related: SYS#7052 --- M s1gw/S1GW_ConnHdlr.ttcn M s1gw/S1GW_Tests.ttcn M s1gw/expected-results.xml 3 files changed, 82 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/59/42359/1
diff --git a/s1gw/S1GW_ConnHdlr.ttcn b/s1gw/S1GW_ConnHdlr.ttcn index 2c900fe..574a2d0 100644 --- a/s1gw/S1GW_ConnHdlr.ttcn +++ b/s1gw/S1GW_ConnHdlr.ttcn @@ -179,6 +179,18 @@ log("eNB connection closed"); }
+/* Close the eNB-side SCTP connection without waiting for pool server events. + * For use in 'impatient eNB' scenarios where the eNB initiates teardown before + * S1 setup completes and no S1APSRV_EVENT_CONN_DOWN is expected in return. */ +function f_ConnHdlr_s1ap_close() runs on ConnHdlr { + f_ConnHdlr_conn_track_disable(); + S1AP_CodecPort_CtrlFunct.f_IPL4_close(S1AP_ENB, g_s1ap_conn_id, + { sctp := c_SctpTuple_S1AP }); + g_s1ap_conn_id := -1; + unmap(self:S1AP_ENB, system:S1AP_CODEC_PT); + log("eNB connection closed"); +} + function f_ConnHdlr_s1ap_expect_shutdown() runs on ConnHdlr { S1AP_ENB.receive(tr_SctpShutDownEvent(g_s1ap_conn_id)); S1AP_ENB.receive(tr_SctpAssocChange(SCTP_SHUTDOWN_COMP, g_s1ap_conn_id)); diff --git a/s1gw/S1GW_Tests.ttcn b/s1gw/S1GW_Tests.ttcn index 5fffb09..d870be8 100644 --- a/s1gw/S1GW_Tests.ttcn +++ b/s1gw/S1GW_Tests.ttcn @@ -1135,6 +1135,71 @@ { S1APSRV_SETUP_REJECT, S1APSRV_SETUP_REJECT, S1APSRV_SETUP_REJECT }); }
+/* MME pool test: eNB connects but disconnects before sending S1SetupReq + * (S1GW is in wait_s1setup_req); S1GW must handle the disconnect cleanly + * and must not attempt to connect to any MME. */ +function f_TC_mme_pool_enb_disc_wait_s1setup_req(charstring id) runs on ConnHdlr { + f_ConnHdlr_s1ap_connect(mp_enb_bind_ip, mp_s1gw_enb_ip); + + /* disconnect before S1SetupReq: S1GW is in wait_s1setup_req, + * no MME connection is ever attempted */ + f_ConnHdlr_s1ap_close(); + setverdict(pass); +} +testcase TC_mme_pool_enb_disc_wait_s1setup_req() runs on test_CT { + f_TC_exec_pool(refers(f_TC_mme_pool_enb_disc_wait_s1setup_req), 1, + { S1APSRV_SETUP_ACCEPT }); +} + +/* MME pool test: eNB connects, sends S1SetupReq, S1GW forwards it to an MME + * (S1GW is in wait_s1setup_rsp), then eNB disconnects before the response arrives. + * S1GW must detect the eNB disconnect and close the MME connection. */ +function f_TC_mme_pool_enb_disc_wait_s1setup_rsp(charstring id) runs on ConnHdlr { + var S1AP_Server_CT vc_srv := g_pars.pool_srvs[0]; + var S1AP_PDU pdu; + timer T; + + f_ConnHdlr_s1ap_connect(mp_enb_bind_ip, mp_s1gw_enb_ip); + f_ConnHdlr_conn_track_disable(); + + /* pre-register with the pool server, then trigger MME selection */ + f_ConnHdlr_s1ap_register_to(g_pars.genb_id, vc_srv); + f_ConnHdlr_tx_s1ap_from_enb(ts_S1AP_SetupReq(g_pars.genb_id, c_SupportedTAs, v32)); + + /* wait until S1GW has forwarded SetupReq to the MME (now in wait_s1setup_rsp) */ + g_s1ap_server := vc_srv; + T.start(10.0); + alt { + [] S1AP_CONN.receive(S1APSRV_Event:S1APSRV_EVENT_CONN_UP) from vc_srv { repeat; } + [] S1AP_CONN.receive(tr_S1AP_SetupReq) from vc_srv -> value pdu { T.stop; } + [] T.timeout { + setverdict(fail, "Timeout waiting for S1SetupReq on pool server"); + Misc_Helpers.f_shutdown(__BFILE__, __LINE__); + } + } + + /* eNB disconnects impatient; S1GW must close the MME connection in response */ + f_ConnHdlr_s1ap_close(); + + T.start(10.0); + alt { + [] S1AP_CONN.receive(S1APSRV_Event:S1APSRV_EVENT_CONN_DOWN) from vc_srv { + T.stop; + setverdict(pass); + } + [] T.timeout { + setverdict(fail, "S1GW did not close MME connection after eNB disconnect"); + Misc_Helpers.f_shutdown(__BFILE__, __LINE__); + } + } + + f_ConnHdlr_s1ap_unregister_from(g_pars.genb_id, vc_srv); +} +testcase TC_mme_pool_enb_disc_wait_s1setup_rsp() runs on test_CT { + f_TC_exec_pool(refers(f_TC_mme_pool_enb_disc_wait_s1setup_rsp), 1, + { S1APSRV_SETUP_ACCEPT }); +} + control { execute( TC_setup() ); execute( TC_setup_multi() ); @@ -1174,6 +1239,8 @@ execute( TC_mme_pool_reject_fallback() ); execute( TC_mme_pool_timeout_fallback() ); execute( TC_mme_pool_all_reject() ); + execute( TC_mme_pool_enb_disc_wait_s1setup_req() ); + execute( TC_mme_pool_enb_disc_wait_s1setup_rsp() ); }
} diff --git a/s1gw/expected-results.xml b/s1gw/expected-results.xml index 28fcf71..0769764 100644 --- a/s1gw/expected-results.xml +++ b/s1gw/expected-results.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<testsuite name='S1GW_Tests' tests='36' failures='0' errors='0' skipped='0' inconc='0' time='MASKED'> +<testsuite name='S1GW_Tests' tests='38' failures='0' errors='0' skipped='0' inconc='0' time='MASKED'> <testcase classname='S1GW_Tests' name='TC_setup' time='MASKED'/> <testcase classname='S1GW_Tests' name='TC_setup_multi' time='MASKED'/> <testcase classname='S1GW_Tests' name='TC_conn_term_by_mme' time='MASKED'/> @@ -38,4 +38,6 @@ <testcase classname='S1GW_Tests' name='TC_mme_pool_reject_fallback' time='MASKED'/> <testcase classname='S1GW_Tests' name='TC_mme_pool_timeout_fallback' time='MASKED'/> <testcase classname='S1GW_Tests' name='TC_mme_pool_all_reject' time='MASKED'/> + <testcase classname='S1GW_Tests' name='TC_mme_pool_enb_disc_wait_s1setup_req' time='MASKED'/> + <testcase classname='S1GW_Tests' name='TC_mme_pool_enb_disc_wait_s1setup_rsp' time='MASKED'/> </testsuite>