blob: 0fb30755d2698f42ce8a3a41b6613bed948942e7 [file] [log] [blame]
/* OsmoS1GW (S1AP Gateway) test suite in TTCN-3
*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
*
* All rights reserved.
*
* Released under the terms of GNU General Public License, Version 2 or
* (at your option) any later version.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
module S1GW_Tests {
import from General_Types all;
import from Osmocom_Types all;
import from Native_Functions all;
import from IPL4asp_Types all;
import from Misc_Helpers all;
import from S1AP_CodecPort all;
import from S1AP_CodecPort_CtrlFunct all;
import from S1AP_Types all;
import from S1AP_Templates all;
import from S1AP_PDU_Descriptions all;
import from S1AP_IEs all;
import from S1AP_PDU_Contents all;
import from S1AP_Constants all;
import from SCTP_Templates all;
import from S1AP_Server all;
modulepar {
charstring mp_s1gw_enb_ip; /* eNB facing address of the S1GW */
charstring mp_s1gw_mme_ip; /* MME facing address of the S1GW */
charstring mp_mme_bind_ip; /* MME address on which we get connections from S1GW */
}
private type record of ConnHdlr ConnHdlrList;
type component ConnHdlr extends S1APSRV_ConnHdlr {
port S1AP_CODEC_PT S1AP_ENB;
var ConnectionId g_s1ap_conn_id := -1;
};
type component test_CT {
timer g_Tguard;
var S1AP_Server_CT vc_S1APSRV;
};
private altstep as_Tguard() runs on test_CT {
[] g_Tguard.timeout {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Tguard timeout");
}
}
template Global_ENB_ID
ts_Global_ENB_ID(integer enb_id := 0,
OCT3 plmn_id := '00f110'O) := {
pLMNidentity := plmn_id,
eNB_ID := {
macroENB_ID := int2bit(enb_id, 20)
},
iE_Extensions := omit
}
function f_init(float Tval := 20.0) runs on test_CT {
g_Tguard.start(Tval);
activate(as_Tguard());
}
function f_init_s1ap_srv() runs on test_CT {
var S1APSRV_ConnParams cpars := {
local_ip := mp_mme_bind_ip,
local_port := 36412
};
vc_S1APSRV := S1AP_Server_CT.create("S1APSRV-" & testcasename());
vc_S1APSRV.start(S1AP_Server.main(cpars));
}
type record ConnHdlrPars {
Global_ENB_ID genb_id
};
template (value) ConnHdlrPars
t_ConnHdlrPars(integer enb_id := 0) := {
genb_id := ts_Global_ENB_ID(enb_id)
}
type function void_fn(ConnHdlrPars pars) runs on ConnHdlr;
function f_ConnHdlr_spawn(void_fn fn, ConnHdlrPars pars)
runs on test_CT return ConnHdlr {
var ConnHdlr vc_conn;
vc_conn := ConnHdlr.create("ConnHdlr-" & testcasename());
if (vc_S1APSRV.running) {
connect(vc_conn:S1AP_CONN, vc_S1APSRV:S1AP_CLIENT);
connect(vc_conn:S1AP_PROC, vc_S1APSRV:S1AP_PROC);
}
vc_conn.start(derefers(fn)(pars));
return vc_conn;
}
function f_ConnHdlr_connect() runs on ConnHdlr {
var Result res;
timer T;
map(self:S1AP_ENB, system:S1AP_CODEC_PT);
/* initiate SCTP connection establishment */
res := S1AP_CodecPort_CtrlFunct.f_IPL4_connect(S1AP_ENB,
mp_s1gw_enb_ip, 36412,
"0.0.0.0", 0, -1,
{ sctp := c_SctpTuple_S1AP });
if (not ispresent(res.connId)) {
setverdict(fail, "Could not create an S1AP socket, check your configuration");
mtc.stop;
}
g_s1ap_conn_id := res.connId;
/* wait for the establishment confirmation */
T.start(2.0);
alt {
[] S1AP_ENB.receive(tr_SctpAssocChange(SCTP_COMM_UP, g_s1ap_conn_id)) {
log("eNB connection established");
}
[] S1AP_ENB.receive(PortEvent:{sctpEvent := ?}) { repeat; }
[] T.timeout {
setverdict(fail, "eNB connection establishment timeout");
self.stop;
}
}
}
function f_ConnHdlr_disconnect() runs on ConnHdlr {
var Result res;
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);
S1AP_CONN.receive(S1APSRV_Event:S1APSRV_EVENT_CONN_DOWN);
log("eNB connection closed");
}
function f_ConnHdlr_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));
S1AP_ENB.receive(PortEvent:{connClosed := ?});
}
function f_ConnHdlr_tx_s1ap_from_enb(template (value) S1AP_PDU pdu)
runs on ConnHdlr {
S1AP_ENB.send(t_S1AP_Send(g_s1ap_conn_id, pdu));
}
function f_ConnHdlr_tx_s1ap_from_mme(template (value) S1AP_PDU pdu)
runs on ConnHdlr {
S1AP_CONN.send(pdu);
}
function f_ConnHdlr_rx_s1ap_from_enb(out S1AP_PDU pdu,
template (present) S1AP_PDU tr_pdu := ?,
float Tval := 0.5)
runs on ConnHdlr {
timer T := Tval;
T.start;
alt {
[] S1AP_CONN.receive(tr_pdu) -> value pdu {
setverdict(pass);
T.stop;
}
[] S1AP_CONN.receive(S1AP_PDU:?) -> value pdu {
setverdict(fail, "Rx unexpected S1AP PDU from eNB: ", pdu);
T.stop;
}
[] T.timeout {
setverdict(fail, "Timeout waiting for S1AP PDU from eNB: ", tr_pdu);
}
}
}
function f_ConnHdlr_rx_s1ap_from_mme(out S1AP_PDU pdu,
template (present) S1AP_PDU tr_pdu := ?,
float Tval := 0.5)
runs on ConnHdlr {
var S1AP_RecvFrom recv;
timer T := Tval;
T.start;
alt {
[] S1AP_ENB.receive(t_S1AP_RecvFrom(tr_pdu)) -> value recv {
pdu := recv.msg;
setverdict(pass);
T.stop;
}
[] S1AP_ENB.receive(t_S1AP_RecvFrom(?)) -> value recv {
pdu := recv.msg;
setverdict(fail, "Rx unexpected S1AP PDU from MME: ", pdu);
T.stop;
}
[] T.timeout {
setverdict(fail, "Timeout waiting for S1AP PDU from MME: ", tr_pdu);
}
}
}
function f_ConnHdlr_setup(Global_ENB_ID genb_id) runs on ConnHdlr {
var S1AP_PDU pdu;
timer T;
var SupportedTAs supported_tas_dummy := {
{
tAC := '0000'O,
broadcastPLMNs := { '00f000'O },
iE_Extensions := omit
}
};
f_ConnHdlr_tx_s1ap_from_enb(ts_S1AP_SetupReq(genb_id,
supported_tas_dummy,
v32));
T.start(1.0);
alt {
[] S1AP_CONN.receive(S1APSRV_Event:S1APSRV_EVENT_CONN_UP) { repeat; }
[] S1AP_CONN.receive(tr_S1AP_SetupReq) {
setverdict(pass);
T.stop;
}
[] S1AP_CONN.receive(S1AP_PDU:?) -> value pdu {
setverdict(fail, "Rx unexpected S1AP PDU: ", pdu);
T.stop;
}
[] T.timeout {
setverdict(fail, "Timeout waiting for S1AP SetupReq");
}
}
}
function f_TC_setup(ConnHdlrPars pars) runs on ConnHdlr {
f_ConnHdlr_register(pars.genb_id);
f_ConnHdlr_connect();
f_ConnHdlr_setup(pars.genb_id);
f_sleep(0.5); /* keep the connection idle for some time */
f_ConnHdlr_disconnect();
f_ConnHdlr_unregister(pars.genb_id);
}
testcase TC_setup() runs on test_CT {
var ConnHdlrPars pars := valueof(t_ConnHdlrPars);
var ConnHdlr vc_conn;
f_init();
f_init_s1ap_srv();
vc_conn := f_ConnHdlr_spawn(refers(f_TC_setup), pars);
vc_conn.done;
}
testcase TC_setup_multi() runs on test_CT {
var ConnHdlrList vc_conns := { };
f_init();
f_init_s1ap_srv();
for (var integer i := 0; i < 42; i := i + 1) {
var ConnHdlrPars pars := valueof(t_ConnHdlrPars(i));
var ConnHdlr vc_conn := f_ConnHdlr_spawn(refers(f_TC_setup), pars);
vc_conns := vc_conns & { vc_conn };
}
for (var integer i := 0; i < 42; i := i + 1) {
vc_conns[i].done;
}
}
/* MME terminates connection, expect S1GW to terminate the eNB connection */
function f_TC_conn_term_by_mme(ConnHdlrPars pars) runs on ConnHdlr {
f_ConnHdlr_register(pars.genb_id);
f_ConnHdlr_connect();
f_ConnHdlr_setup(pars.genb_id);
f_sleep(0.5); /* keep the connection idle for some time */
/* MME (S1AP_Server_CT) terminates connection */
f_ConnHdlr_close_conn(pars.genb_id);
/* expect our eNB connection to be released gracefully */
f_ConnHdlr_expect_shutdown();
f_ConnHdlr_unregister(pars.genb_id);
}
testcase TC_conn_term_by_mme() runs on test_CT {
var ConnHdlrPars pars := valueof(t_ConnHdlrPars);
var ConnHdlr vc_conn;
f_init();
f_init_s1ap_srv();
vc_conn := f_ConnHdlr_spawn(refers(f_TC_conn_term_by_mme), pars);
vc_conn.done;
}
/* MME is not available, expect S1GW to terminate the eNB connection */
function f_TC_conn_term_mme_unavail(ConnHdlrPars pars) runs on ConnHdlr {
/* establish an eNB connection to the S1GW */
f_ConnHdlr_connect();
/* expect our eNB connection to be released gracefully */
f_ConnHdlr_expect_shutdown();
setverdict(pass);
}
testcase TC_conn_term_mme_unavail() runs on test_CT {
var ConnHdlrPars pars := valueof(t_ConnHdlrPars);
var ConnHdlr vc_conn;
f_init();
f_init_s1ap_srv();
vc_S1APSRV.stop;
vc_conn := f_ConnHdlr_spawn(refers(f_TC_conn_term_mme_unavail), pars);
vc_conn.done;
}
function f_TC_e_rab_setup(ConnHdlrPars pars) runs on ConnHdlr {
const integer mme_id := 7;
const integer enb_id := 9;
const integer erab_id := 6;
var S1AP_PDU pdu;
f_ConnHdlr_register(pars.genb_id);
f_ConnHdlr_connect();
f_ConnHdlr_setup(pars.genb_id);
log("eNB -> [S1GW] -> MME: E-RAB SETUP REQUEST");
var template (value) E_RABToBeSetupListBearerSUReq items_req;
var E_RABToBeSetupItemBearerSUReq item_req := {
e_RAB_ID := erab_id,
e_RABlevelQoSParameters := {
qCI := 5,
allocationRetentionPriority := {
priorityLevel := 1,
pre_emptionCapability := shall_not_trigger_pre_emption,
pre_emptionVulnerability := not_pre_emptable,
iE_Extensions := omit
},
gbrQosInformation := omit,
iE_Extensions := omit
},
transportLayerAddress := -,
gTP_TEID := f_rnd_octstring(4),
nAS_PDU := ''O,
iE_Extensions := omit
};
/* eNB -> S1GW */
item_req.transportLayerAddress := oct2bit(f_inet_addr("1.2.3.4"));
items_req := ts_S1AP_RABToBeSetupListBearerSUReq(item_req);
f_ConnHdlr_tx_s1ap_from_enb(ts_S1AP_RABSetupReq(mme_id, enb_id, items_req));
/* S1GW -> MME */
item_req.transportLayerAddress := oct2bit(f_inet_addr(mp_s1gw_mme_ip));
items_req := ts_S1AP_RABToBeSetupListBearerSUReq(item_req);
f_ConnHdlr_rx_s1ap_from_enb(pdu, tr_S1AP_RABSetupReq(mme_id, enb_id, items_req));
log("eNB <- [S1GW] <- MME: E-RAB SETUP RESPONSE");
var template (value) E_RABSetupListBearerSURes items_res;
var E_RABSetupItemBearerSURes item_res := {
e_RAB_ID := erab_id,
transportLayerAddress := -,
gTP_TEID := f_rnd_octstring(4),
iE_Extensions := omit
};
/* MME -> S1GW */
item_res.transportLayerAddress := oct2bit(f_inet_addr("4.3.2.1"));
items_res := ts_S1AP_RABSetupListBearerSURes(item_res);
f_ConnHdlr_tx_s1ap_from_mme(ts_S1AP_RABSetupRsp(mme_id, enb_id, items_res));
/* S1GW -> eNB */
item_res.transportLayerAddress := oct2bit(f_inet_addr(mp_s1gw_enb_ip));
items_res := ts_S1AP_RABSetupListBearerSURes(item_res);
f_ConnHdlr_rx_s1ap_from_mme(pdu, tr_S1AP_RABSetupRsp(mme_id, enb_id, items_res));
f_ConnHdlr_disconnect();
f_ConnHdlr_unregister(pars.genb_id);
}
testcase TC_e_rab_setup() runs on test_CT {
var ConnHdlrPars pars := valueof(t_ConnHdlrPars);
var ConnHdlr vc_conn;
f_init();
f_init_s1ap_srv();
vc_conn := f_ConnHdlr_spawn(refers(f_TC_e_rab_setup), pars);
vc_conn.done;
}
control {
execute( TC_setup() );
execute( TC_setup_multi() );
execute( TC_conn_term_by_mme() );
execute( TC_conn_term_mme_unavail() );
execute( TC_e_rab_setup() );
}
}