mme: Extend MME tests to also include S1AP interface testing

The tests so far only tested SGsAP alone, which doesn't get us very
far.  Let's add interfacing with the S1, too.

Change-Id: Id54071b901db720a0f88b91cdc5a8e09bea0fb58
This commit is contained in:
Harald Welte 2019-07-11 22:51:45 +08:00 committed by laforge
parent c918e4e373
commit 95333a16eb
6 changed files with 399 additions and 25 deletions

18
mme/MME_Tests.cfg Normal file
View File

@ -0,0 +1,18 @@
[ORDERED_INCLUDE]
# Common configuration, shared between test suites
"../Common.cfg"
# testsuite specific configuration, not expected to change
"./MME_Tests.default"
# Local configuration below
[LOGGING]
[TESTPORT_PARAMETERS]
[MODULE_PARAMETERS]
[MAIN_CONTROLLER]
[EXECUTE]
MME_Tests.control

12
mme/MME_Tests.default Normal file
View File

@ -0,0 +1,12 @@
[LOGGING]
FileMask := LOG_ALL | TTCN_MATCHING;
mtc.FileMask := ERROR | WARNING | PARALLEL | VERDICTOP;
[TESTPORT_PARAMETERS]
[MODULE_PARAMETERS]
[MAIN_CONTROLLER]
[EXECUTE]

View File

@ -10,31 +10,84 @@
module MME_Tests {
import from General_Types all;
import from S1AP_Types all;
import from S1AP_Templates all;
import from S1AP_Emulation all;
import from S1AP_PDU_Descriptions all;
import from S1AP_IEs all;
import from NAS_EPS_Types all;
import from NAS_Templates all;
import from SGsAP_Types all;
import from SGsAP_Templates all;
import from SGsAP_Emulation all;
import from LTE_CryptoFunctions all;
import from L3_Templates all;
import from DNS_Helpers all;
import from Osmocom_Types all;
friend module MME_Tests_SGsAP;
/* (maximum) number of emulated eNBs */
const integer NUM_ENB := 3;
/* (maximum) number of emulated UEs */
const integer NUM_UE := 3;
/* parameters of emulated ENB */
type record EnbParams {
Global_ENB_ID global_enb_id,
integer cell_identity,
SupportedTAs supported_tas
}
/* parameters of emulated UE */
type record UeParams {
hexstring imsi
}
type component MTC_CT {
/* S1 intreface of emulated ENBs */
var EnbParams g_enb_pars[NUM_ENB];
var S1AP_Emulation_CT vc_S1AP[NUM_ENB];
port S1AP_PT S1AP_UNIT[NUM_ENB];
port S1APEM_PROC_PT S1AP_PROC[NUM_ENB];
/* SGs interface of emulated MSC/VLR */
var SGsAP_Emulation_CT vc_SGsAP;
port SGsAP_PT SGsAP_UNIT;
port SGsAPEM_PROC_PT SGsAP_PROC;
var UeParams g_ue_pars[NUM_UE];
}
type component ConnHdlr extends SGsAP_ConnHdlr {
type component ConnHdlr extends S1AP_ConnHdlr, SGsAP_ConnHdlr {
var ConnHdlrPars g_pars;
timer g_Tguard := 30.0;
}
type record ConnHdlrPars {
hexstring imsi
/* copied over from MTC_CT on start of component */
EnbParams enb_pars[NUM_ENB],
/* copied over from MTC_CT on start of component */
UeParams ue_pars,
/* currently used MME (index into enb_pars, S1AP, ...) */
integer mme_idx
}
modulepar {
/* S1 interface */
charstring mp_mme_ip := "127.0.0.1";
integer mp_mme_s1ap_port := 36412;
charstring mp_s1_local_ip := "127.0.0.1";
integer mp_s1_local_port := 50000;
/* SGs interface */
charstring mp_sgs_local_ip := "127.0.0.1";
integer mp_sgs_local_port := 29118;
charstring mp_vlr_name := "vlr.example.net";
@ -68,11 +121,96 @@ friend function f_init_sgsap(charstring id) runs on MTC_CT {
vc_SGsAP.start(SGsAP_Emulation.main(ops, pars, id));
}
/* send incoming unit data messages (like reset) to global S1AP_UNIT port */
friend function S1apForwardUnitdataCallback(S1AP_PDU msg)
runs on S1AP_Emulation_CT return template S1AP_PDU {
S1AP_UNIT.send(msg);
return omit;
}
friend function S1apCreateCallback(S1AP_PDU msg, template (omit) MME_UE_S1AP_ID mme_id,
template (omit) ENB_UE_S1AP_ID enb_id, charstring id)
runs on S1AP_Emulation_CT return S1AP_ConnHdlr
{
setverdict(fail, "implement this");
mtc.stop;
}
friend function f_init_one_enb(charstring id, integer num := 0) runs on MTC_CT {
id := id & "-S1AP" & int2str(num);
var S1APOps ops := {
create_cb := refers(S1apCreateCallback),
unitdata_cb := refers(S1apForwardUnitdataCallback)
}
var S1AP_conn_parameters pars := {
remote_ip := mp_mme_ip,
remote_sctp_port := mp_mme_s1ap_port,
local_ip := mp_s1_local_ip,
local_sctp_port := mp_s1_local_port + num,
role := NAS_ROLE_UE
}
var PLMNidentity plmn_id := '00f110'O;
var EnbParams enb_pars := {
global_enb_id := {
pLMNidentity := plmn_id,
eNB_ID := {
macroENB_ID := int2bit(num, 20)
},
iE_Extensions := omit
},
cell_identity := num,
supported_tas := {
{
tAC := int2oct(12345, 2),
broadcastPLMNs := { plmn_id },
iE_Extensions := omit
}
}
};
g_enb_pars[num] := enb_pars;
vc_S1AP[num] := S1AP_Emulation_CT.create(id);
map(vc_S1AP[num]:S1AP, system:S1AP_CODEC_PT);
connect(vc_S1AP[num]:S1AP_PROC, self:S1AP_PROC[num]);
connect(vc_S1AP[num]:S1AP_UNIT, self:S1AP_UNIT[num]);
vc_S1AP[num].start(S1AP_Emulation.main(ops, pars, id));
S1AP_UNIT[num].receive(S1APEM_Event:{up_down:=S1APEM_EVENT_UP});
}
friend function f_init_one_ue(inout UeParams uep, integer imsi_suffix) {
uep := {
imsi := f_gen_imsi(imsi_suffix)
}
}
friend function f_init_s1ap(charstring id, integer imsi_suffix) runs on MTC_CT {
var integer i;
for (i := 0; i < NUM_ENB; i := i+1) {
f_init_one_enb(id, i);
}
for (i := 0; i < NUM_UE; i := i+1) {
f_init_one_ue(g_ue_pars[i], i*1000 + imsi_suffix);
}
}
friend template (value) TAI ts_enb_S1AP_TAI(EnbParams enb) := {
pLMNidentity := enb.global_enb_id.pLMNidentity,
tAC := enb.supported_tas[0].tAC,
iE_Extensions := omit
}
friend template (value) EUTRAN_CGI ts_enb_S1AP_CGI(EnbParams enb) := {
pLMNidentity := enb.global_enb_id.pLMNidentity,
cell_ID := int2bit(enb.cell_identity, 28),
iE_Extensions := omit
}
/* generate parameters for a connection handler */
friend function f_init_pars(integer imsi_suffix)
friend function f_init_pars(integer ue_idx := 0)
runs on MTC_CT return ConnHdlrPars {
var ConnHdlrPars pars := {
imsi := f_gen_imsi(imsi_suffix)
enb_pars := g_enb_pars,
ue_pars := g_ue_pars[ue_idx],
mme_idx := 0
};
return pars;
}
@ -86,9 +224,14 @@ runs on MTC_CT return ConnHdlr {
var charstring id := testcasename() & int2str(s1ap_idx);
vc_conn := ConnHdlr.create(id);
/* S1AP part */
connect(vc_conn:S1AP, vc_S1AP[s1ap_idx]:S1AP_CLIENT);
connect(vc_conn:S1AP_PROC, vc_S1AP[s1ap_idx]:S1AP_PROC);
if (isbound(vc_SGsAP)) {
/* SGsAP part */
connect(vc_conn:SGsAP, vc_SGsAP:SGsAP_CLIENT);
connect(vc_conn:SGsAP_PROC, vc_SGsAP:SGsAP_PROC);
}
/* We cannot use vc_conn.start(f_init_handler(fn, id, pars)); as we cannot have
* a stand-alone 'derefers()' call, see https://www.eclipse.org/forums/index.php/t/1091364/ */
@ -110,8 +253,209 @@ friend function f_init_handler(ConnHdlrPars pars, float t_guard := 30.0) runs on
/* start guard timre and activate it as default */
g_Tguard.start(t_guard);
activate(as_Tguard());
if (SGsAP_PROC.checkstate("Connected")) {
/* Route all SGsAP mesages for our IMSIto us */
f_create_sgsap_expect(pars.imsi);
f_create_sgsap_expect(pars.ue_pars.imsi);
}
}
friend function f_s1ap_setup(integer idx := 0, template Cause cause := omit) runs on MTC_CT {
var template (present) Cause exp_cause;
var boolean exp_fail := false;
timer T := 5.0;
if (not istemplatekind(cause, "omit")) {
exp_fail := true;
exp_cause := cause;
}
S1AP_UNIT[idx].send(ts_S1AP_SetupReq(g_enb_pars[idx].global_enb_id,
g_enb_pars[idx].supported_tas, v32));
T.start;
alt {
[exp_fail] S1AP_UNIT[idx].receive(tr_S1AP_SetupFail(exp_cause)) {
setverdict(pass);
}
[not exp_fail] S1AP_UNIT[idx].receive(tr_S1AP_SetupResp) {
setverdict(pass);
}
[] S1AP_UNIT[idx].receive {
setverdict(fail, "Received unexpected S1AP");
}
[] T.timeout {
setverdict(fail, "Timeout waiting for S1AP Setup result");
}
}
}
/* Unsuccessful S1 Setup procedure to MME (wrong PLMN) */
testcase TC_s1ap_setup_wrong_plmn() runs on MTC_CT {
var charstring id := testcasename();
f_init_s1ap(id, 1);
g_enb_pars[0].global_enb_id.pLMNidentity := '62F224'O;
f_s1ap_setup(0, {misc:=unknown_PLMN});
}
/* Unsuccessful S1 Setup procedure to MME (wrong PLMN) */
testcase TC_s1ap_setup_wrong_tac() runs on MTC_CT {
var charstring id := testcasename();
f_init_s1ap(id, 2);
g_enb_pars[0].supported_tas[0].broadcastPLMNs[0] := '62F224'O;
f_s1ap_setup(0, {misc:=unknown_PLMN});
}
/* Successful S1 Setup procedure to MME */
testcase TC_s1ap_setup() runs on MTC_CT {
var charstring id := testcasename();
f_init_s1ap(id, 3);
f_s1ap_setup(0);
}
private const EPS_QualityOfServiceV c_NAS_defaultQoS := {
qCI := '00'O,
maxBitRateUplink := omit,
maxBitRateDownlink := omit,
guaranteedBitRateUplink := omit,
guaranteedBitRateDownlink := omit,
maxBitRateUplinkExt := omit,
maxBitRateDownlinkExt := omit,
guaranteedBitRateUplinkExt := omit,
guaranteedBitRateDownlinkExt := omit,
maxBitRateUplinkExt2 := omit,
maxBitRateDownlinkExt2 := omit,
guaranteedBitRateUplinkExt2 := omit,
guaranteedBitRateDownlinkExt2 := omit
};
private const UENetworkCapabilityV c_NAS_defaultUeNetCap := {
eEA := '10000000'B,
eIA := '11000000'B,
uEA := omit,
uIA := omit,
uCS2 := omit,
nF := omit,
vCC := omit,
lCS := omit,
lPP := omit,
aCC_CSFB := omit,
h245_ASH := omit,
proSe := omit,
proSe_dd := omit,
proSe_dc := omit,
proSe_relay := omit,
cP_CIoT := omit,
uP_CIoT := omit,
s1_Udata := omit,
eRwoPDN := omit,
hC_CP_CIoT := omit,
ePCO := omit,
multipleDRB := omit,
v2XPC5 := omit,
restrictEC := omit,
cPbackoff := omit,
dCNR := omit,
n1Mode := omit,
sGC := omit,
spare1 := omit,
spare := omit
};
private const octetstring c_NAS_defaultAPN := '00'O;
private altstep as_s1ap_handle_auth() runs on ConnHdlr {
var PDU_NAS_EPS rx_nas;
[] S1AP.receive(tr_NAS_AuthReq) -> value rx_nas {
/* static XRES result as we fixed the HSS RAND value and always have the following
RAND: 20080c3818183b522614162c07601d0d
AUTN: f11b89a2a8be00001f9c526f3d75d44c
IK: 11329aae8e8d2941bb226b2061137c58
CK: 740d62df9803eebde5120acf358433d0
RES: 6a91970e838fd079
SRES: e91e4777
Kc: 3b0f999e42198874
SQN: 32
IND: 0
*/
/* KASME: 95AFAD9A0D29AFAA079A9451DF7161D7EE4CBF2AF9387F766D058BB6B44B905D */
const OCT16 ck := '740d62df9803eebde5120acf358433d0'O;
const OCT16 ik := '11329aae8e8d2941bb226b2061137c58'O;
const OCT16 autn := 'f11b89a2a8be00001f9c526f3d75d44c'O;
const OCT8 res := '6a91970e838fd079'O;
const OCT3 plmn_id := '00F110'O;
const OCT6 sqn := '000000000020'O;
const OCT6 ak := substr(autn, 0, 6) xor4b sqn;
var octetstring kasme := f_kdf_kasme(ck, ik, plmn_id, sqn, ak);
var S1APEM_Config cfg := {
set_nas_keys := {
k_nas_int := f_kdf_nas_int(1, kasme),
k_nas_enc := f_kdf_nas_enc(1, kasme)
}
};
S1AP.send(cfg);
S1AP.send(ts_NAS_AuthResp(res));
}
}
private altstep as_s1ap_handle_sec_mode() runs on ConnHdlr {
var PDU_NAS_EPS rx_nas;
var NAS_SecurityAlgorithmsV alg := {
typeOfIntegrityProtection := '001'B,
spare1 := '0'B,
typeOfCiphering := '000'B,
spare2 := '0'B
};
var NAS_KeySetIdentifierV kset_id := {
identifier := '000'B,
tSC := '0'B
};
[] S1AP.receive(tr_NAS_SecModeCmd(alg, kset_id, ?)) {
S1AP.send(ts_NAS_SecModeCmpl);
}
}
private function f_TC_attach(ConnHdlrPars pars) runs on ConnHdlr {
f_init_handler(pars);
var template (value) EPS_MobileIdentityV mi := ts_NAS_MobileId_IMSI('001010000000001'H);
var template (value) PDU_NAS_EPS nas_esm, nas_emm;
/*
nas_esm := ts_NAS_ActDefEpsBearCtxReq(bearer_id := '0000'B, proc_tid := int2bit(1,8),
qos := c_NAS_defaultQoS, apn := c_NAS_defaultAPN,
addr_type := '000'B, addr_info := ''O);
*/
nas_esm := ts_NAS_PdnConnReq(bearer_id := '0000'B, proc_tid := int2bit(1,8),
pdn_type := NAS_PDN_T_IPv4, req_type := '001'B);
nas_emm := ts_NAS_AttachRequest(att_type := '000'B, kset_id := '000'B, mobile_id := mi,
ue_net_cap := c_NAS_defaultUeNetCap,
esm_enc := enc_PDU_NAS_EPS(valueof(nas_esm)));
var template (value) S1AP_PDU tx;
tx := ts_S1AP_InitialUE(p_eNB_value := 0, p_nasPdu := enc_PDU_NAS_EPS(valueof(nas_emm)),
p_tAI := ts_enb_S1AP_TAI(g_pars.enb_pars[g_pars.mme_idx]),
p_eUTRAN_CGI := ts_enb_S1AP_CGI(g_pars.enb_pars[g_pars.mme_idx]),
p_rrcCause := mo_Signalling);
S1AP.send(tx);
as_s1ap_handle_auth();
as_s1ap_handle_sec_mode();
f_sleep(10.0);
}
testcase TC_s1ap_attach() runs on MTC_CT {
var charstring id := testcasename();
f_init_s1ap(id, 4);
f_s1ap_setup(0);
var ConnHdlrPars pars := f_init_pars(ue_idx := 0);
var ConnHdlr vc_conn;
vc_conn := f_start_handler_with_pars(refers(f_TC_attach), pars);
vc_conn.done;
}
control {
execute( TC_s1ap_setup_wrong_plmn() );
execute( TC_s1ap_setup_wrong_tac() );
execute( TC_s1ap_setup() );
}

View File

@ -48,23 +48,23 @@ function f_sgsap_page(Service_Indicator serv_ind, template (omit) OCT4 tmsi,
if (not istemplatekind(exp_cause, "omit")) {
exp_success := false;
}
SGsAP.send(ts_SGsAP_PAGING_REQ(g_pars.imsi, vlr_name, serv_ind, tmsi));
SGsAP.send(ts_SGsAP_PAGING_REQ(g_pars.ue_pars.imsi, vlr_name, serv_ind, tmsi));
alt {
/* we expect success */
[exp_success] SGsAP.receive(tr_SGsAP_SERVICE_REQ(g_pars.imsi, serv_ind, ?)) {
[exp_success] SGsAP.receive(tr_SGsAP_SERVICE_REQ(g_pars.ue_pars.imsi, serv_ind, ?)) {
setverdict(pass);
}
[exp_success] SGsAP.receive(tr_SGsAP_PAGING_REJ(g_pars.imsi, ?)) {
[exp_success] SGsAP.receive(tr_SGsAP_PAGING_REJ(g_pars.ue_pars.imsi, ?)) {
setverdict(fail, "Received unexpected PAGING REJECT");
}
/* we expect failure */
[not exp_success] SGsAP.receive(tr_SGsAP_SERVICE_REQ(g_pars.imsi, serv_ind, ?)) {
[not exp_success] SGsAP.receive(tr_SGsAP_SERVICE_REQ(g_pars.ue_pars.imsi, serv_ind, ?)) {
setverdict(fail, "Received SERVICE REQ waiting for PAGING REJECT");
}
[not exp_success] SGsAP.receive(tr_SGsAP_PAGING_REJ(g_pars.imsi, exp_cause)) {
[not exp_success] SGsAP.receive(tr_SGsAP_PAGING_REJ(g_pars.ue_pars.imsi, exp_cause)) {
setverdict(pass);
}
[not exp_success] SGsAP.receive(tr_SGsAP_PAGING_REJ(g_pars.imsi, ?)) {
[not exp_success] SGsAP.receive(tr_SGsAP_PAGING_REJ(g_pars.ue_pars.imsi, ?)) {
setverdict(fail, "Received unexpected PAGING REJECT cause");
}
[] SGsAP.receive {
@ -80,21 +80,21 @@ function f_sgsap_alert(template (omit) SGs_Cause exp_cause) runs on ConnHdlr{
if (not istemplatekind(exp_cause, "omit")) {
exp_success := false;
}
SGsAP.send(ts_SGsAP_ALERT_REQ(g_pars.imsi));
SGsAP.send(ts_SGsAP_ALERT_REQ(g_pars.ue_pars.imsi));
alt {
[exp_success] SGsAP.receive(tr_SGsAP_ALERT_ACK(g_pars.imsi)) {
[exp_success] SGsAP.receive(tr_SGsAP_ALERT_ACK(g_pars.ue_pars.imsi)) {
setverdict(pass);
}
[exp_success] SGsAP.receive(tr_SGsAP_ALERT_REJECT(g_pars.imsi, ?)) -> value rx {
[exp_success] SGsAP.receive(tr_SGsAP_ALERT_REJECT(g_pars.ue_pars.imsi, ?)) -> value rx {
setverdict(fail, "Received unexpected ALERT REJECT ", rx);
}
[not exp_success] SGsAP.receive(tr_SGsAP_ALERT_ACK(g_pars.imsi)) {
[not exp_success] SGsAP.receive(tr_SGsAP_ALERT_ACK(g_pars.ue_pars.imsi)) {
setverdict(fail, "Received unexpected ALERT ACK");
}
[not exp_success] SGsAP.receive(tr_SGsAP_ALERT_REJECT(g_pars.imsi, exp_cause)) {
[not exp_success] SGsAP.receive(tr_SGsAP_ALERT_REJECT(g_pars.ue_pars.imsi, exp_cause)) {
setverdict(pass)
}
[not exp_success] SGsAP.receive(tr_SGsAP_ALERT_REJECT(g_pars.imsi, ?)) -> value rx {
[not exp_success] SGsAP.receive(tr_SGsAP_ALERT_REJECT(g_pars.ue_pars.imsi, ?)) -> value rx {
setverdict(fail, "Received ALERT REJECT with unexpected cause ", rx);
}
[] SGsAP.receive {
@ -168,7 +168,7 @@ private function f_TC_sgsap_alert(ConnHdlrPars pars) runs on ConnHdlr {
/* TODO: register subscriber on S1 */
f_sgsap_alert(omit);
/* TOOD: do something on S1 triggering UE ACT IND */
SGsAP.receive(tr_SGsAP_UE_ACT_IND(g_pars.imsi));
SGsAP.receive(tr_SGsAP_UE_ACT_IND(g_pars.ue_pars.imsi));
}
testcase TC_sgsap_alert() runs on MTC_CT {
var ConnHdlrPars pars;

View File

@ -38,14 +38,14 @@ gen_links $DIR $FILES
DIR=../library/s1ap
FILES="S1AP_CommonDataTypes.asn S1AP_Constants.asn S1AP_Containers.asn S1AP_IEs.asn S1AP_PDU_Contents.asn
S1AP_PDU_Descriptions.asn "
FILES+="S1AP_EncDec.cc S1AP_Types.ttcn "
FILES+="S1AP_EncDec.cc S1AP_Types.ttcn S1AP_Templates.ttcn "
gen_links $DIR $FILES
DIR=../library
FILES="Misc_Helpers.ttcn General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn Native_Functions.ttcn Native_FunctionDefs.cc "
FILES+="SGsAP_Templates.ttcn SGsAP_CodecPort.ttcn SGsAP_CodecPort_CtrlFunct.ttcn SGsAP_CodecPort_CtrlFunctDef.cc SGsAP_Emulation.ttcn DNS_Helpers.ttcn "
FILES+="L3_Templates.ttcn "
FILES+="S1AP_CodecPort.ttcn "
FILES+="S1AP_CodecPort.ttcn S1AP_CodecPort_CtrlFunctDef.cc S1AP_CodecPort_CtrlFunct.ttcn S1AP_Emulation.ttcn "
FILES+="NAS_Templates.ttcn "
gen_links $DIR $FILES

View File

@ -1,6 +1,6 @@
#!/bin/sh
FILES="*.ttcn *.asn *.c IPL4asp_PT.cc IPL4asp_discovery.cc Native_FunctionDefs.cc SGsAP_CodecPort_CtrlFunctDef.cc TCCConversion.cc TCCEncoding.cc TCCInterface.cc TELNETasp_PT.cc S1AP_EncDec.cc LTE_CryptoFunctionDefs.cc "
FILES="*.ttcn *.asn *.c IPL4asp_PT.cc IPL4asp_discovery.cc Native_FunctionDefs.cc SGsAP_CodecPort_CtrlFunctDef.cc S1AP_CodecPort_CtrlFunctDef.cc TCCConversion.cc TCCEncoding.cc TCCInterface.cc TELNETasp_PT.cc S1AP_EncDec.cc LTE_CryptoFunctionDefs.cc "
export CPPFLAGS_TTCN3=""