module SGSN_Tests { import from General_Types all; import from Osmocom_Types all; import from Native_Functions all; import from NS_Types all; import from NS_Emulation all; import from BSSGP_Types all; import from BSSGP_Emulation all; import from Osmocom_Gb_Types all; import from MobileL3_CommonIE_Types all; import from MobileL3_GMM_SM_Types all; import from MobileL3_Types all; import from L3_Templates all; import from L3_Common all; import from GSUP_Emulation all; import from GSUP_Types all; import from IPA_Emulation all; import from GTP_Emulation all; import from GTP_Templates all; import from GTP_CodecPort all; import from GTPC_Types all; import from GTPU_Types all; import from LLC_Types all; import from LLC_Templates all; import from SNDCP_Types all; import from TELNETasp_PortType all; import from Osmocom_VTY_Functions all; import from GSM_RR_Types all; modulepar { /* IP/port on which we run our internal GSUP/HLR emulation */ charstring mp_hlr_ip := "127.0.0.1"; integer mp_hlr_port := 4222; charstring mp_ggsn_ip := "127.0.0.2"; }; type record GbInstance { NS_CT vc_NS, BSSGP_CT vc_BSSGP, BssgpConfig cfg }; type component test_CT { var GbInstance g_gb[3]; var GSUP_Emulation_CT vc_GSUP; var IPA_Emulation_CT vc_GSUP_IPA; /* only to get events from IPA underneath GSUP */ port IPA_CTRL_PT GSUP_IPA_EVENT; var GTP_Emulation_CT vc_GTP; port TELNETasp_PT SGSNVTY; var boolean g_initialized := false; }; type component BSSGP_ConnHdlr extends BSSGP_Client_CT, GSUP_ConnHdlr, GTP_ConnHdlr { var BSSGP_ConnHdlrPars g_pars; timer g_Tguard; } type record SGSN_ConnHdlrNetworkPars { boolean expect_ptmsi, boolean expect_auth, boolean expect_ciph }; type record BSSGP_ConnHdlrPars { /* IMEI of the simulated ME */ hexstring imei, /* IMEI of the simulated MS */ hexstring imsi, /* MSISDN of the simulated MS (probably unused) */ hexstring msisdn, /* P-TMSI allocated to the simulated MS */ OCT4 p_tmsi optional, OCT3 p_tmsi_sig optional, /* TLLI of the simulated MS */ OCT4 tlli, OCT4 tlli_old optional, RoutingAreaIdentificationV ra optional, BssgpCellId bssgp_cell_id, AuthVector vec optional, SGSN_ConnHdlrNetworkPars net, float t_guard }; private function f_init_gb(inout GbInstance gb, charstring id) runs on test_CT { gb.vc_NS := NS_CT.create(id & "-NS"); gb.vc_BSSGP := BSSGP_CT.create(id & "-BSSGP"); /* connect lower end of BSSGP emulation with NS upper port */ connect(gb.vc_BSSGP:BSCP, gb.vc_NS:NS_SP); /* connect lower end of NS emulation to NS codec port (on top of IPL4) */ map(gb.vc_NS:NSCP, system:NS_CODEC_PORT); gb.vc_NS.start(NSStart()); gb.vc_BSSGP.start(BssgpStart(gb.cfg)); } private function f_init_gsup(charstring id) runs on test_CT { id := id & "-GSUP"; var GsupOps ops := { create_cb := refers(GSUP_Emulation.ExpectedCreateCallback) }; vc_GSUP_IPA := IPA_Emulation_CT.create(id & "-IPA"); vc_GSUP := GSUP_Emulation_CT.create(id); map(vc_GSUP_IPA:IPA_PORT, system:IPA_CODEC_PT); connect(vc_GSUP:GSUP, vc_GSUP_IPA:IPA_GSUP_PORT); /* we use this hack to get events like ASP_IPA_EVENT_UP */ connect(vc_GSUP_IPA:IPA_CTRL_PORT, self:GSUP_IPA_EVENT); vc_GSUP.start(GSUP_Emulation.main(ops, id)); vc_GSUP_IPA.start(IPA_Emulation.main_server(mp_hlr_ip, mp_hlr_port)); /* wait for incoming connection to GSUP port before proceeding */ timer T := 10.0; T.start; alt { [] GSUP_IPA_EVENT.receive(t_ASP_IPA_EVT_UD(ASP_IPA_EVENT_UP)) { } [] T.timeout { setverdict(fail, "No connection to GSUP Port"); self.stop; } } } private function f_init_gtp(charstring id) runs on test_CT { id := id & "-GTP"; var GtpEmulationCfg gtp_cfg := { gtpc_bind_ip := mp_ggsn_ip, gtpc_bind_port := GTP1C_PORT, gtpu_bind_ip := mp_ggsn_ip, gtpu_bind_port := GTP1U_PORT, sgsn_role := false }; vc_GTP := GTP_Emulation_CT.create(id); vc_GTP.start(GTP_Emulation.main(gtp_cfg)); } private function f_init_vty() runs on test_CT { map(self:SGSNVTY, system:SGSNVTY); f_vty_set_prompts(SGSNVTY); f_vty_transceive(SGSNVTY, "enable"); f_vty_config(SGSNVTY, "sgsn", "auth-policy remote"); } function f_init(BcdMccMnc mcc_mnc := '26242F'H) runs on test_CT { if (g_initialized == true) { return; } g_initialized := true; g_gb[0].cfg := { nsei := 96, bvci := 196, cell_id := { ra_id := { lai := { mcc_mnc := mcc_mnc, lac := 13135}, rac := 0 }, cell_id := 20960 }, sgsn_role := false }; f_init_gb(g_gb[0], "SGSN_Test-Gb0"); f_init_gsup("SGSN_Test"); f_init_gtp("SGSN_Test"); f_init_vty(); } type function void_fn(charstring id) runs on BSSGP_ConnHdlr; /* helper function to create, connect and start a BSSGP_ConnHdlr component */ function f_start_handler(void_fn fn, charstring id, GbInstance gb, integer imsi_suffix, float t_guard := 30.0) runs on test_CT return BSSGP_ConnHdlr { var BSSGP_ConnHdlr vc_conn; var SGSN_ConnHdlrNetworkPars net_pars := { expect_ptmsi := true, expect_auth := true, expect_ciph := false }; var BSSGP_ConnHdlrPars pars := { imei := f_gen_imei(imsi_suffix), imsi := f_gen_imsi(imsi_suffix), msisdn := f_gen_msisdn(imsi_suffix), p_tmsi := omit, p_tmsi_sig := omit, tlli := f_gprs_tlli_random(), tlli_old := omit, ra := omit, bssgp_cell_id := gb.cfg.cell_id, vec := omit, net := net_pars, t_guard := t_guard }; vc_conn := BSSGP_ConnHdlr.create(id); connect(vc_conn:BSSGP, gb.vc_BSSGP:BSSGP_SP); connect(vc_conn:BSSGP_PROC, gb.vc_BSSGP:BSSGP_PROC); connect(vc_conn:GSUP, vc_GSUP:GSUP_CLIENT); connect(vc_conn:GSUP_PROC, vc_GSUP:GSUP_PROC); connect(vc_conn:GTP, vc_GTP:CLIENT); connect(vc_conn:GTP_PROC, vc_GTP:CLIENT_PROC); vc_conn.start(f_handler_init(fn, id, pars)); return vc_conn; } private altstep as_Tguard() runs on BSSGP_ConnHdlr { [] g_Tguard.timeout { setverdict(fail, "Tguard timeout"); self.stop; } } /* first function called in every ConnHdlr */ private function f_handler_init(void_fn fn, charstring id, BSSGP_ConnHdlrPars pars) runs on BSSGP_ConnHdlr { /* do some common stuff like setting up g_pars */ g_pars := pars; /* register with BSSGP core */ f_bssgp_client_register(g_pars.imsi, g_pars.tlli, g_pars.bssgp_cell_id); /* tell GSUP dispatcher to send this IMSI to us */ f_create_gsup_expect(hex2str(g_pars.imsi)); /* tell GTP dispatcher to send this IMSI to us */ f_gtp_register_imsi(g_pars.imsi); g_Tguard.start(pars.t_guard); activate(as_Tguard()); /* call the user-supplied test case function */ fn.apply(id); f_bssgp_client_unregister(g_pars.imsi); } /* TODO: * Detach without Attach * SM procedures without attach / RAU * ATTACH / RAU ** with / without authentication ** with / without P-TMSI allocation * re-transmissions of LLC frames * PDP Context activation ** with different GGSN config in SGSN VTY ** with different PDP context type (v4/v6/v46) ** timeout from GGSN ** multiple / secondary PDP context */ testcase TC_wait_ns_up() runs on test_CT { f_init(); f_sleep(20.0); } altstep as_mm_identity() runs on BSSGP_ConnHdlr { var MobileL3_CommonIE_Types.MobileIdentityLV mi; [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ID_REQ('001'B))) { mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); BSSGP.send(ts_GMM_ID_RESP(mi)); repeat; } [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ID_REQ('010'B))) { mi := valueof(ts_MI_IMEI_LV(g_pars.imei)); BSSGP.send(ts_GMM_ID_RESP(mi)); repeat; } } /* perform GMM authentication (if expected) */ function f_gmm_auth () runs on BSSGP_ConnHdlr { var BssgpDecoded bd; var PDU_L3_MS_SGSN l3_mo; var PDU_L3_SGSN_MS l3_mt; var default di := activate(as_mm_identity()); if (g_pars.net.expect_auth) { g_pars.vec := f_gen_auth_vec_2g(); var GSUP_IE auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G(g_pars.vec.rand, g_pars.vec.sres, g_pars.vec.kc)); GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)); GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple)); BSSGP.receive(tr_BD_L3_MT(tr_GMM_AUTH_REQ(g_pars.vec.rand))) -> value bd; l3_mt := bd.l3_mt; var BIT4 ac_ref := l3_mt.msgs.gprs_mm.authenticationAndCipheringRequest.acReferenceNumber.valueField; l3_mo := valueof(ts_GMM_AUTH_RESP_2G(ac_ref, g_pars.vec.sres)); if (ispresent(l3_mt.msgs.gprs_mm.authenticationAndCipheringRequest.imeisvRequest) and l3_mt.msgs.gprs_mm.authenticationAndCipheringRequest.imeisvRequest.valueField == '001'B) { l3_mo.msgs.gprs_mm.authenticationAndCipheringResponse.imeisv := valueof(ts_MI_IMEISV_TLV(g_pars.imei & '0'H)); } BSSGP.send(l3_mo); } else { /* wait for identity procedure */ f_sleep(1.0); } deactivate(di); } function f_upd_ptmsi_and_tlli(OCT4 p_tmsi) runs on BSSGP_ConnHdlr { g_pars.p_tmsi := p_tmsi; /* update TLLI */ g_pars.tlli_old := g_pars.tlli; g_pars.tlli := g_pars.p_tmsi or4b 'c0000000'O; f_bssgp_client_llgmm_assign(g_pars.tlli_old, g_pars.tlli); } function f_process_attach_accept(PDU_GMM_AttachAccept aa) runs on BSSGP_ConnHdlr { /* mandatory IE */ var hexstring aa_plmn := f_RAI_to_plmn_hexstr(aa.routingAreaIdentification); if (not (g_pars.bssgp_cell_id.ra_id.lai.mcc_mnc == aa_plmn)) { setverdict(fail, "mismatching PLMN in Attach Accept: " & hex2str(aa_plmn) & "; expected " & hex2str(g_pars.bssgp_cell_id.ra_id.lai.mcc_mnc)); self.stop; } g_pars.ra := aa.routingAreaIdentification; if (ispresent(aa.allocatedPTMSI)) { if (not g_pars.net.expect_ptmsi) { setverdict(fail, "unexpected P-TMSI allocation"); self.stop; } f_upd_ptmsi_and_tlli(aa.allocatedPTMSI.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi.octets); } if (ispresent(aa.msIdentity)) { setverdict(fail, "unexpected TMSI allocation in non-combined attach"); self.stop; } /* P-TMSI.sig */ if (ispresent(aa.ptmsiSignature)) { g_pars.p_tmsi_sig := aa.ptmsiSignature.valueField; } /* updateTimer */ // aa.readyTimer /* T3302, T3319, T3323, T3312_ext, T3324 */ } function f_process_rau_accept(PDU_GMM_RoutingAreaUpdateAccept ra) runs on BSSGP_ConnHdlr { /* mandatory IE */ g_pars.ra := ra.routingAreaId; if (ispresent(ra.allocatedPTMSI)) { if (not g_pars.net.expect_ptmsi) { setverdict(fail, "unexpected P-TMSI allocation"); self.stop; } f_upd_ptmsi_and_tlli(ra.allocatedPTMSI.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi.octets); } if (ispresent(ra.msIdentity)) { setverdict(fail, "unexpected TMSI allocation in non-combined attach"); self.stop; } /* P-TMSI.sig */ if (ispresent(ra.ptmsiSignature)) { g_pars.p_tmsi_sig := ra.ptmsiSignature.valueField; } /* updateTimer */ // aa.readyTimer /* T3302, T3319, T3323, T3312_ext, T3324 */ } function f_random_RAI(HEX0_3n mcc := '262'H, HEX0_3n mnc := '42'H) return RoutingAreaIdentificationV { return f_RAI(mcc, mnc, f_rnd_octstring(2), f_rnd_octstring(1)); } /* return a MobileIdentityLV: P-TMSI if we have one, IMSI otherwise */ private function f_mi_get_lv() runs on BSSGP_ConnHdlr return MobileL3_CommonIE_Types.MobileIdentityLV { if (ispresent(g_pars.p_tmsi)) { return valueof(ts_MI_TMSI_LV(g_pars.p_tmsi)); } else { return valueof(ts_MI_IMSI_LV(g_pars.imsi)); } } private function f_gmm_gsup_lu_isd() runs on BSSGP_ConnHdlr { var GSUP_PDU gsup; /* Expect MSC to perform LU with HLR */ GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi)); gsup := valueof(ts_GSUP_ISD_REQ(g_pars.imsi, g_pars.msisdn)); gsup.ies := gsup.ies & { valueof(ts_GSUP_IE_PdpInfo(char2oct("*"), '0121'O, ''O)) }; GSUP.send(gsup); GSUP.receive(tr_GSUP_ISD_RES(g_pars.imsi)); GSUP.send(ts_GSUP_UL_RES(g_pars.imsi)); } private function f_TC_attach(charstring id) runs on BSSGP_ConnHdlr { var BssgpDecoded bd; var RoutingAreaIdentificationV old_ra := f_random_RAI(); BSSGP.send(ts_GMM_ATTACH_REQ(f_mi_get_lv(), old_ra, false, false, omit, omit)); f_gmm_auth(); /* Expect MSC to perform LU with HLR */ f_gmm_gsup_lu_isd(); BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_ACCEPT('001'B, ?, ?))) -> value bd { f_process_attach_accept(bd.l3_mt.msgs.gprs_mm.attachAccept); } /* FIXME: Extract P-TMSI, if any. Only send Complete if necessary */ BSSGP.send(ts_GMM_ATTACH_COMPL); setverdict(pass); } testcase TC_attach() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_attach), testcasename(), g_gb[0], 1); vc_conn.done; } testcase TC_attach_mnc3() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init('023042'H); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_attach), testcasename(), g_gb[0], 1001); vc_conn.done; } /* MS never responds to ID REQ, expect ATTACH REJECT */ private function f_TC_attach_auth_id_timeout(charstring id) runs on BSSGP_ConnHdlr { var RoutingAreaIdentificationV old_ra := f_random_RAI(); BSSGP.send(ts_GMM_ATTACH_REQ(f_mi_get_lv(), old_ra, false, false, omit, omit)); alt { [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ID_REQ(?))) { /* don't send ID Response */ repeat; } [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_REJECT('09'O))) { setverdict(pass); } [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_REJECT(?))) { setverdict(fail, "Wrong Attach Reject Cause"); } } } testcase TC_attach_auth_id_timeout() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); vc_conn := f_start_handler(refers(f_TC_attach_auth_id_timeout), testcasename(), g_gb[0], 2, 40.0); vc_conn.done; } /* HLR never responds to SAI REQ, expect ATTACH REJECT */ private function f_TC_attach_auth_sai_timeout(charstring id) runs on BSSGP_ConnHdlr { var RoutingAreaIdentificationV old_ra := f_random_RAI(); BSSGP.send(ts_GMM_ATTACH_REQ(f_mi_get_lv(), old_ra, false, false, omit, omit)); alt { [] as_mm_identity(); [] GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)); { } } /* don't send SAI-response from HLR */ BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_REJECT(?))); setverdict(pass); } testcase TC_attach_auth_sai_timeout() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); vc_conn := f_start_handler(refers(f_TC_attach_auth_sai_timeout), testcasename(), g_gb[0], 3); vc_conn.done; } /* HLR rejects SAI, expect ATTACH REJECT */ private function f_TC_attach_auth_sai_reject(charstring id) runs on BSSGP_ConnHdlr { var RoutingAreaIdentificationV old_ra := f_random_RAI(); BSSGP.send(ts_GMM_ATTACH_REQ(f_mi_get_lv(), old_ra, false, false, omit, omit)); alt { [] as_mm_identity(); [] GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)); { GSUP.send(ts_GSUP_SAI_ERR(g_pars.imsi, 23)); } } BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_REJECT(?))); setverdict(pass); } testcase TC_attach_auth_sai_reject() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); vc_conn := f_start_handler(refers(f_TC_attach_auth_sai_reject), testcasename(), g_gb[0], 4); vc_conn.done; } /* HLR never responds to UL REQ, expect ATTACH REJECT */ private function f_TC_attach_gsup_lu_timeout(charstring id) runs on BSSGP_ConnHdlr { var BssgpDecoded bd; var RoutingAreaIdentificationV old_ra := f_random_RAI(); BSSGP.send(ts_GMM_ATTACH_REQ(f_mi_get_lv(), old_ra, false, false, omit, omit)); f_gmm_auth(); /* Expect MSC to perform LU with HLR */ GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi)); /* Never follow-up with ISD_REQ or UL_RES */ alt { [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_REJECT(?))) { setverdict(pass); } [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_ACCEPT('001'B, ?, ?))) -> value bd { f_process_attach_accept(bd.l3_mt.msgs.gprs_mm.attachAccept); setverdict(fail); } } } testcase TC_attach_gsup_lu_timeout() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_attach_gsup_lu_timeout), testcasename(), g_gb[0], 5); vc_conn.done; } /* HLR rejects UL REQ, expect ATTACH REJECT */ private function f_TC_attach_gsup_lu_reject(charstring id) runs on BSSGP_ConnHdlr { var BssgpDecoded bd; var RoutingAreaIdentificationV old_ra := f_random_RAI(); BSSGP.send(ts_GMM_ATTACH_REQ(f_mi_get_lv(), old_ra, false, false, omit, omit)); f_gmm_auth(); /* Expect MSC to perform LU with HLR */ GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi)) { GSUP.send(ts_GSUP_UL_ERR(g_pars.imsi, 0)); } alt { [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_REJECT(?))) { setverdict(pass); } [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_ACCEPT('001'B, ?, ?))) -> value bd { f_process_attach_accept(bd.l3_mt.msgs.gprs_mm.attachAccept); setverdict(fail); } } } testcase TC_attach_gsup_lu_reject() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_attach_gsup_lu_reject), testcasename(), g_gb[0], 6); vc_conn.done; } /* Attempt of combined GPRS + IMSI attach: network should ACK only GPRS attach */ private function f_TC_attach_combined(charstring id) runs on BSSGP_ConnHdlr { var BssgpDecoded bd; var RoutingAreaIdentificationV old_ra := f_random_RAI(); BSSGP.send(ts_GMM_ATTACH_REQ(f_mi_get_lv(), old_ra, true, false, omit, omit)); f_gmm_auth(); /* Expect MSC to perform LU with HLR */ f_gmm_gsup_lu_isd(); BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_ACCEPT('001'B, ?, ?))) -> value bd { f_process_attach_accept(bd.l3_mt.msgs.gprs_mm.attachAccept); } BSSGP.send(ts_GMM_ATTACH_COMPL); setverdict(pass); } testcase TC_attach_combined() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_attach_combined), testcasename(), g_gb[0], 7); vc_conn.done; } /* Attempt of GPRS ATTACH in 'accept all' mode */ private function f_TC_attach_accept_all(charstring id) runs on BSSGP_ConnHdlr { var BssgpDecoded bd; var RoutingAreaIdentificationV old_ra := f_random_RAI(); g_pars.net.expect_auth := false; BSSGP.send(ts_GMM_ATTACH_REQ(f_mi_get_lv(), old_ra, false, false, omit, omit)); f_gmm_auth(); BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_ACCEPT('001'B, ?, ?))) -> value bd { f_process_attach_accept(bd.l3_mt.msgs.gprs_mm.attachAccept); } BSSGP.send(ts_GMM_ATTACH_COMPL); setverdict(pass); } testcase TC_attach_accept_all() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); f_vty_config(SGSNVTY, "sgsn", "auth-policy accept-all"); vc_conn := f_start_handler(refers(f_TC_attach_accept_all), testcasename(), g_gb[0], 8); vc_conn.done; } /* Attempt of GPRS ATTACH in 'accept all' mode */ private function f_TC_attach_closed_foreign(charstring id) runs on BSSGP_ConnHdlr { var RoutingAreaIdentificationV old_ra := f_random_RAI(); /* Simulate a foreign IMSI */ g_pars.imsi := '001010123456789'H; f_bssgp_client_register(g_pars.imsi, g_pars.tlli, g_pars.bssgp_cell_id); g_pars.net.expect_auth := false; BSSGP.send(ts_GMM_ATTACH_REQ(f_mi_get_lv(), old_ra, false, false, omit, omit)); alt { [] as_mm_identity(); [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_REJECT('07'O))) { setverdict(pass); } [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_REJECT(?))) { setverdict(pass); } } } testcase TC_attach_closed() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); f_vty_config(SGSNVTY, "sgsn", "auth-policy closed"); /* test with foreign IMSI: Must Reject */ vc_conn := f_start_handler(refers(f_TC_attach_closed_foreign), testcasename(), g_gb[0], 9); vc_conn.done; /* test with home IMSI: Must Accept */ vc_conn := f_start_handler(refers(f_TC_attach_accept_all), testcasename(), g_gb[0], 10); vc_conn.done; } /* Routing Area Update from Unknown TLLI -> REJECT */ private function f_TC_rau_unknown(charstring id) runs on BSSGP_ConnHdlr { var RoutingAreaIdentificationV old_ra := f_random_RAI(); BSSGP.send(ts_GMM_RAU_REQ(f_mi_get_lv(), GPRS_UPD_T_RA, old_ra, false, omit, omit)); alt { [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_RAU_REJECT('0a'O))) { setverdict(pass); } /* FIXME: Expect XID RESET? */ [] BSSGP.receive { repeat; } } } testcase TC_rau_unknown() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_rau_unknown), testcasename(), g_gb[0], 11); vc_conn.done; } private function f_TC_attach_rau(charstring id) runs on BSSGP_ConnHdlr { var BssgpDecoded bd; /* first perform regular attach */ f_TC_attach(id); /* then send RAU */ BSSGP.send(ts_GMM_RAU_REQ(f_mi_get_lv(), GPRS_UPD_T_RA, g_pars.ra, false, omit, omit)); alt { [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_RAU_ACCEPT)) -> value bd { f_process_rau_accept(bd.l3_mt.msgs.gprs_mm.routingAreaUpdateAccept); setverdict(pass); } [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_RAU_REJECT)) { setverdict(fail, "Unexpected RAU Reject"); } [] BSSGP.receive { repeat; } } } testcase TC_attach_rau() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_attach_rau), testcasename(), g_gb[0], 12); vc_conn.done; } /* general GPRS DETACH helper */ function f_detach_mo(BIT3 detach_type, boolean power_off, boolean expect_purge) runs on BSSGP_ConnHdlr { var BssgpDecoded bd; timer T := 5.0; BSSGP.send(ts_GMM_DET_REQ_MO(detach_type, power_off)); if (expect_purge) { GSUP.receive(tr_GSUP_PURGE_MS_REQ(g_pars.imsi, OSMO_GSUP_CN_DOMAIN_PS)); GSUP.send(ts_GSUP_PURGE_MS_RES(g_pars.imsi)); } T.start; alt { [not expect_purge] GSUP.receive(tr_GSUP_PURGE_MS_REQ(?)) { setverdict(fail, "Unexpected GSUP PURGE MS for unregistered TLLI"); } [power_off] BSSGP.receive(tr_BD_L3_MT(tr_GMM_DET_ACCEPT_MT)) -> value bd { g_pars.ra := omit; setverdict(fail, "Unexpected ATTACH ACCEPT in no-power-off DETACH"); /* TODO: check if any PDP contexts are deactivated on network side? */ } [power_off] T.timeout { setverdict(pass); } [not power_off] BSSGP.receive(tr_BD_L3_MT(tr_GMM_DET_ACCEPT_MT)) -> value bd { g_pars.ra := omit; setverdict(pass); /* TODO: check if any PDP contexts are deactivated on network side? */ } [] BSSGP.receive { repeat; } } } /* IMSI DETACH (non-power-off) for unknown TLLI */ private function f_TC_detach_unknown_nopoweroff(charstring id) runs on BSSGP_ConnHdlr { f_detach_mo(c_GMM_DTT_MO_GPRS, false, false); } testcase TC_detach_unknown_nopoweroff() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_detach_unknown_nopoweroff), testcasename(), g_gb[0], 13); vc_conn.done; } /* IMSI DETACH (power-off) for unknown TLLI */ private function f_TC_detach_unknown_poweroff(charstring id) runs on BSSGP_ConnHdlr { f_detach_mo(c_GMM_DTT_MO_GPRS, true, false); } testcase TC_detach_unknown_poweroff() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_detach_unknown_poweroff), testcasename(), g_gb[0], 14); vc_conn.done; } /* IMSI DETACH (non-power-off) for known TLLI */ private function f_TC_detach_nopoweroff(charstring id) runs on BSSGP_ConnHdlr { /* first perform regular attach */ f_TC_attach(id); f_detach_mo(c_GMM_DTT_MO_GPRS, false, true); } testcase TC_detach_nopoweroff() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_detach_nopoweroff), testcasename(), g_gb[0], 15); vc_conn.done; } /* IMSI DETACH (power-off) for known TLLI */ private function f_TC_detach_poweroff(charstring id) runs on BSSGP_ConnHdlr { /* first perform regular attach */ f_TC_attach(id); f_detach_mo(c_GMM_DTT_MO_GPRS, true, true); } testcase TC_detach_poweroff() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); f_sleep(1.0); vc_conn := f_start_handler(refers(f_TC_detach_poweroff), testcasename(), g_gb[0], 16); vc_conn.done; } type record PdpActPars { BIT3 tid, /* L3 Transaction ID */ BIT4 nsapi, /* SNDCP NSAPI */ BIT4 sapi, /* LLC SAPI */ QoSV qos, /* QoS parameters */ PDPAddressV addr, /* IP address */ octetstring apn optional, /* APN name */ ProtocolConfigOptionsV pco optional, /* protoco config opts */ OCT1 exp_rej_cause optional, /* expected SM reject cause */ OCT1 gtp_resp_cause, /* GTP response cause */ OCT4 chg_id, /* GTP Charging Identifier */ OCT4 ggsn_tei_c, /* GGSN TEI Control*/ OCT4 ggsn_tei_u, /* GGSN TEI User */ octetstring ggsn_ip_c, /* GGSN IP Control */ octetstring ggsn_ip_u, /* GGSN IP User */ OCT4 sgsn_tei_c optional, /* SGSN TEI Control */ OCT4 sgsn_tei_u optional, /* SGSN TEI User */ octetstring sgsn_ip_c optional, /* SGSN IP Control */ octetstring sgsn_ip_u optional /* SGSN IP USer */ }; private function f_process_gtp_ctx_act_req(inout PdpActPars apars, PDU_GTPC gtpc) runs on BSSGP_ConnHdlr { var GTPC_PDUs gtpc_rx := gtpc.gtpc_pdu; apars.sgsn_tei_c := gtpc_rx.createPDPContextRequest.teidControlPlane.teidControlPlane; apars.sgsn_tei_u := gtpc_rx.createPDPContextRequest.teidDataI.teidDataI; apars.sgsn_ip_c := gtpc_rx.createPDPContextRequest.sgsn_addr_signalling.addressf; apars.sgsn_ip_u := gtpc_rx.createPDPContextRequest.sgsn_addr_traffic.addressf; f_gtp_register_teid(apars.ggsn_tei_c); f_gtp_register_teid(apars.ggsn_tei_u); } function f_pdp_ctx_act(inout PdpActPars apars) runs on BSSGP_ConnHdlr { var boolean exp_rej := ispresent(apars.exp_rej_cause); var Gtp1cUnitdata g_ud; BSSGP.send(ts_SM_ACT_PDP_REQ(apars.tid, apars.nsapi, apars.sapi, apars.qos, apars.addr, apars.apn, apars.pco)); GTP.receive(tr_GTPC_MsgType(?, createPDPContextRequest, ?)) -> value g_ud { f_process_gtp_ctx_act_req(apars, g_ud.gtpc); var integer seq_nr := oct2int(g_ud.gtpc.opt_part.sequenceNumber); GTP.send(ts_GTPC_CreatePdpResp(g_ud.peer, seq_nr, apars.sgsn_tei_c, apars.gtp_resp_cause, apars.ggsn_tei_c, apars.ggsn_tei_u, apars.nsapi, apars.ggsn_ip_c, apars.ggsn_ip_u, apars.chg_id)); } alt { [exp_rej] BSSGP.receive(tr_BD_L3_MT(tr_SM_ACT_PDP_REJ(apars.tid, apars.exp_rej_cause))) { setverdict(pass); } [exp_rej] BSSGP.receive(tr_BD_L3_MT(tr_SM_ACT_PDP_ACCEPT)) { setverdict(fail, "Unexpected PDP CTX ACT ACC"); } [not exp_rej] BSSGP.receive(tr_BD_L3_MT(tr_SM_ACT_PDP_REJ(apars.tid, ?))) { setverdict(fail, "Unexpected PDP CTX ACT FAIL"); } [not exp_rej] BSSGP.receive(tr_BD_L3_MT(tr_SM_ACT_PDP_ACCEPT(apars.tid, apars.sapi))) { setverdict(pass); } [] as_xid(apars); } } function f_pdp_ctx_deact_mo(inout PdpActPars apars, OCT1 cause) runs on BSSGP_ConnHdlr { var boolean exp_rej := ispresent(apars.exp_rej_cause); var Gtp1cUnitdata g_ud; BSSGP.send(ts_SM_DEACT_PDP_REQ_MO(apars.tid, cause, false, omit)); GTP.receive(tr_GTPC_MsgType(?, deletePDPContextRequest, apars.ggsn_tei_c)) -> value g_ud { var integer seq_nr := oct2int(g_ud.gtpc.opt_part.sequenceNumber); BSSGP.clear; GTP.send(ts_GTPC_DeletePdpResp(g_ud.peer, seq_nr, apars.sgsn_tei_c, '7F'O)); } alt { [] BSSGP.receive(tr_BD_L3_MT(tr_SM_DEACT_PDP_ACCEPT_MT(apars.tid))) { setverdict(pass); } [] as_xid(apars); } } function f_pdp_ctx_deact_mt(inout PdpActPars apars, OCT1 cause) runs on BSSGP_ConnHdlr { var Gtp1cUnitdata g_ud; var integer seq_nr := 23; var GtpPeer peer := valueof(ts_GtpPeerC(apars.sgsn_ip_c)); BSSGP.clear; GTP.send(ts_GTPC_DeletePDP(peer, seq_nr, apars.sgsn_tei_c, apars.nsapi, omit)); interleave { [] BSSGP.receive(tr_BD_L3_MT(tr_SM_DEACT_PDP_REQ_MT(apars.tid, ?))) { BSSGP.send(ts_SM_DEACT_PDP_ACCEPT_MO(apars.tid)); } [] GTP.receive(tr_GTPC_MsgType(?, deletePDPContextResponse, apars.ggsn_tei_c)) { } } } /* Table 10.5.156/3GPP TS 24.008 */ template (value) QoSV t_QosDefault := { reliabilityClass := '011'B, /* unacknowledged GTP+LLC, acknowledged RLC */ delayClass := '100'B, /* best effort */ spare1 := '00'B, precedenceClass := '010'B, /* normal */ spare2 := '0'B, peakThroughput := '0000'B, /* subscribed */ meanThroughput := '00000'B, /* subscribed */ spare3 := '000'B, deliverErroneusSDU := omit, deliveryOrder := omit, trafficClass := omit, maxSDUSize := omit, maxBitrateUplink := omit, maxBitrateDownlink := omit, sduErrorRatio := omit, residualBER := omit, trafficHandlingPriority := omit, transferDelay := omit, guaranteedBitRateUplink := omit, guaranteedBitRateDownlink := omit, sourceStatisticsDescriptor := omit, signallingIndication := omit, spare4 := omit, maxBitrateDownlinkExt := omit, guaranteedBitRateDownlinkExt := omit, maxBitrateUplinkExt := omit, guaranteedBitRateUplinkExt := omit, maxBitrateDownlinkExt2 := omit, guaranteedBitRateDownlinkExt2 := omit, maxBitrateUplinkExt2 := omit, guaranteedBitRateUplinkExt2 := omit } /* 10.5.6.4 / 3GPP TS 24.008 */ template (value) PDPAddressV t_AddrIPv4dyn := { pdpTypeOrg := '0001'B, /* IETF */ spare := '0000'B, pdpTypeNum := '21'O, /* IPv4 */ addressInfo := omit } template (value) PDPAddressV t_AddrIPv6dyn := { pdpTypeOrg := '0001'B, /* IETF */ spare := '0000'B, pdpTypeNum := '53'O, /* IPv6 */ addressInfo := omit } template (value) PdpActPars t_PdpActPars(charstring ggsn_ip) := { tid := '000'B, nsapi := '0101'B, /* < 5 are reserved */ sapi := '0011'B, /* 3/5/9/11 */ qos := t_QosDefault, addr := t_AddrIPv4dyn, apn := omit, pco := omit, exp_rej_cause := omit, gtp_resp_cause := int2oct(128, 1), chg_id := f_rnd_octstring(4), /* FIXME: make below dynamic !! */ ggsn_tei_c := f_rnd_octstring(4), ggsn_tei_u := f_rnd_octstring(4), ggsn_ip_c := f_inet_addr(ggsn_ip), ggsn_ip_u := f_inet_addr(ggsn_ip), sgsn_tei_c := omit, sgsn_tei_u := omit, sgsn_ip_c := omit, sgsn_ip_u := omit } template (value) GtpPeer ts_GtpPeerU(octetstring ip) := { connId := 1, remName := f_inet_ntoa(ip), remPort := GTP1U_PORT } template (value) GtpPeer ts_GtpPeerC(octetstring ip) := { connId := 1, remName := f_inet_ntoa(ip), remPort := GTP1C_PORT } private function f_gtpu_send(inout PdpActPars apars, octetstring payload) runs on BSSGP_ConnHdlr { var GtpPeer peer := valueof(ts_GtpPeerU(apars.sgsn_ip_u)); GTP.send(ts_GTP1U_GPDU(peer, 0 /*seq*/, apars.sgsn_tei_u, payload)); } private altstep as_xid(PdpActPars apars) runs on BSSGP_ConnHdlr { [] BSSGP.receive(tr_BD_LLC(tr_LLC_XID(?, apars.sapi))) { repeat; } } template PDU_SN tr_SN_UD(template BIT4 nsapi, template octetstring payload) := { pDU_SN_UNITDATA := { nsapi := nsapi, moreBit := ?, snPduType := '1'B, firstSegmentIndicator := ?, spareBit := ?, pcomp := ?, dcomp := ?, npduNumber := ?, segmentNumber := ?, npduNumberContinued := ?, dataSegmentSnUnitdataPdu := payload } } /* simple case: single segment, no compression */ template (value) PDU_SN ts_SN_UD(BIT4 nsapi, octetstring payload) := { pDU_SN_UNITDATA := { nsapi := nsapi, moreBit := '0'B, snPduType := '1'B, firstSegmentIndicator := '1'B, spareBit := '0'B, pcomp := '0000'B, dcomp := '0000'B, npduNumber := '0000'B, segmentNumber := '0000'B, npduNumberContinued := '00'O, dataSegmentSnUnitdataPdu := payload } } /* Transceive given 'payload' as MT message from GTP -> OsmoSGSN -> Gb */ private function f_gtpu_xceive_mt(inout PdpActPars apars, octetstring payload) runs on BSSGP_ConnHdlr { /* Send PDU via GTP from our simulated GGSN to the SGSN */ f_gtpu_send(apars, payload); /* Expect PDU via BSSGP/LLC on simulated PCU from SGSN */ alt { [] as_xid(apars); [] BSSGP.receive(tr_BD_SNDCP(apars.sapi, tr_SN_UD(apars.nsapi, payload))); } } private function f_gtpu_xceive_mo(inout PdpActPars apars, octetstring payload) runs on BSSGP_ConnHdlr { /* Send PDU via SNDCP/LLC/BSSGP/NS via simulated MS/PCU to the SGSN */ var GtpPeer peer := valueof(ts_GtpPeerU(apars.sgsn_ip_u)); var PDU_SN sndcp := valueof(ts_SN_UD(apars.nsapi, payload)); BSSGP.send(ts_LLC_UI(enc_PDU_SN(sndcp), apars.sapi, '0'B, 0)); f_gtpu_send(apars, payload); /* Expect PDU via GTP from SGSN on simulated GGSN */ alt { [] GTP.receive(tr_GTPU_GPDU(peer, apars.ggsn_tei_u, payload)); } } private function f_TC_attach_pdp_act(charstring id) runs on BSSGP_ConnHdlr { var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip)); /* first perform regular attach */ f_TC_attach(id); f_pdp_ctx_act(apars); } testcase TC_attach_pdp_act() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); vc_conn := f_start_handler(refers(f_TC_attach_pdp_act), testcasename(), g_gb[0], 17); vc_conn.done; } /* PDP Context activation for not-attached subscriber; expect fail */ private function f_TC_pdp_act_unattached(charstring id) runs on BSSGP_ConnHdlr { var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip)); BSSGP.send(ts_SM_ACT_PDP_REQ(apars.tid, apars.nsapi, apars.sapi, apars.qos, apars.addr, apars.apn, apars.pco)); alt { /* We might want toalso actually expect a PDPC CTX ACT REJ? */ [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_DET_REQ_MT(?, ?))) { setverdict(pass); } [] GTP.receive(tr_GTPC_MsgType(?, createPDPContextRequest, ?)) { setverdict(fail, "Unexpected GTP PDP CTX ACT"); } [] BSSGP.receive(tr_BD_L3_MT(tr_SM_ACT_PDP_ACCEPT(?, ?))) { setverdict(fail, "Unexpected SM PDP CTX ACT ACK"); } [] BSSGP.receive { repeat; } } } testcase TC_pdp_act_unattached() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); vc_conn := f_start_handler(refers(f_TC_pdp_act_unattached), testcasename(), g_gb[0], 18); vc_conn.done; } /* ATTACH + PDP CTX ACT + user plane traffic */ private function f_TC_attach_pdp_act_user(charstring id) runs on BSSGP_ConnHdlr { var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip)); /* first perform regular attach */ f_TC_attach(id); /* then activate PDP context */ f_pdp_ctx_act(apars); /* then transceive a downlink PDU */ f_gtpu_xceive_mt(apars, f_rnd_octstring(100)); f_gtpu_xceive_mo(apars, f_rnd_octstring(200)); } testcase TC_attach_pdp_act_user() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); vc_conn := f_start_handler(refers(f_TC_attach_pdp_act_user), testcasename(), g_gb[0], 19); vc_conn.done; } /* ATTACH + PDP CTX ACT; reject from GGSN */ private function f_TC_attach_pdp_act_ggsn_reject(charstring id) runs on BSSGP_ConnHdlr { var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip)); apars.gtp_resp_cause := int2oct(199, 1); /* no resources available */ apars.exp_rej_cause := '1a'O; /* insufficient resources */ /* first perform regular attach */ f_TC_attach(id); /* then activate PDP context */ f_pdp_ctx_act(apars); } testcase TC_attach_pdp_act_ggsn_reject() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); vc_conn := f_start_handler(refers(f_TC_attach_pdp_act_ggsn_reject), testcasename(), g_gb[0], 20); vc_conn.done; } /* ATTACH + PDP CTX ACT + user plane traffic + PDP CTX DEACT in MO direction */ private function f_TC_attach_pdp_act_user_deact_mo(charstring id) runs on BSSGP_ConnHdlr { var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip)); /* first perform regular attach */ f_TC_attach(id); /* then activate PDP context */ f_pdp_ctx_act(apars); /* then transceive a downlink PDU */ f_gtpu_xceive_mt(apars, f_rnd_octstring(100)); f_gtpu_xceive_mo(apars, f_rnd_octstring(200)); f_pdp_ctx_deact_mo(apars, '00'O); } testcase TC_attach_pdp_act_user_deact_mo() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); vc_conn := f_start_handler(refers(f_TC_attach_pdp_act_user_deact_mo), testcasename(), g_gb[0], 21); vc_conn.done; } /* ATTACH + PDP CTX ACT + user plane traffic + PDP CTX DEACT in MT direction */ private function f_TC_attach_pdp_act_user_deact_mt(charstring id) runs on BSSGP_ConnHdlr { var PdpActPars apars := valueof(t_PdpActPars(mp_ggsn_ip)); /* first perform regular attach */ f_TC_attach(id); /* then activate PDP context */ f_pdp_ctx_act(apars); /* then transceive a downlink PDU */ f_gtpu_xceive_mt(apars, f_rnd_octstring(100)); f_gtpu_xceive_mo(apars, f_rnd_octstring(200)); f_pdp_ctx_deact_mt(apars, '00'O); } testcase TC_attach_pdp_act_user_deact_mt() runs on test_CT { var BSSGP_ConnHdlr vc_conn; f_init(); vc_conn := f_start_handler(refers(f_TC_attach_pdp_act_user_deact_mt), testcasename(), g_gb[0], 22); vc_conn.done; } control { execute( TC_attach() ); execute( TC_attach_mnc3() ); execute( TC_attach_auth_id_timeout() ); execute( TC_attach_auth_sai_timeout() ); execute( TC_attach_auth_sai_reject() ); execute( TC_attach_gsup_lu_timeout() ); execute( TC_attach_gsup_lu_reject() ); execute( TC_attach_combined() ); execute( TC_attach_accept_all() ); execute( TC_attach_closed() ); execute( TC_rau_unknown() ); execute( TC_attach_rau() ); execute( TC_detach_unknown_nopoweroff() ); execute( TC_detach_unknown_poweroff() ); execute( TC_detach_nopoweroff() ); execute( TC_detach_poweroff() ); execute( TC_attach_pdp_act() ); execute( TC_pdp_act_unattached() ); execute( TC_attach_pdp_act_user() ); execute( TC_attach_pdp_act_ggsn_reject() ); execute( TC_attach_pdp_act_user_deact_mo() ); execute( TC_attach_pdp_act_user_deact_mt() ); } }