module BSC_ConnectionHandler { import from General_Types all; import from Osmocom_Types all; import from GSM_Types all; import from SCCPasp_Types all; import from BSSAP_Types all; import from BSSMAP_Emulation all; import from BSSMAP_Templates all; import from GSUP_Types all; import from GSUP_Emulation all; import from MNCC_Types all; import from MNCC_Emulation all; import from MobileL3_Types all; import from MobileL3_CommonIE_Types all; import from MobileL3_MM_Types all; import from L3_Templates all; /* this component represents a single subscriber connection */ type component BSC_ConnHdlr extends BSSAP_ConnHdlr, MNCC_ConnHdlr, GSUP_ConnHdlr { var BSC_ConnHdlrPars g_pars; } type record BSC_ConnHdlrPars { SCCP_PAR_Address sccp_addr_own, SCCP_PAR_Address sccp_addr_peer, BSSMAP_IE_CellIdentifier cell_id, hexstring imei, hexstring imsi, hexstring msisdn, OCT4 tmsi optional, BSSMAP_IE_ClassmarkInformationType2 cm2, BSSMAP_IE_ClassmarkInformationType3 cm3 optional, octetstring kc optional }; /* Callback function from general BSSMAP_Emulation whenever a connectionless * BSSMAP message arrives. Canreturn a PDU_BSSAPthat should be sent in return */ private function BscUnitdataCallback(PDU_BSSAP bssap) runs on BSSMAP_Emulation_CT return template PDU_BSSAP { var template PDU_BSSAP resp := omit; log("BSSMAP_BscUnitdataCallback"); /* answer all RESET with RESET ACK */ if (match(bssap, tr_BSSMAP_Reset)){ log("BSSMAP_BscUnitdataCallback: Responding to RESET with RESET-ACK"); resp := ts_BSSMAP_ResetAck; } /* FIXME: Handle paging, etc. */ return resp; } const BssmapOps BSC_BssmapOps := { /* Create call-back for inbound connections from MSC (hand-over) */ create_cb := refers(BSSMAP_Emulation.ExpectedCreateCallback), unitdata_cb := refers(BscUnitdataCallback), decode_dtap := true, role_ms := true } private function MnccUnitdataCallback(MNCC_PDU mncc) runs on MNCC_Emulation_CT return template MNCC_PDU { log("Ignoring MNCC", mncc); return omit; } const MnccOps BCC_MnccOps := { create_cb := refers(MNCC_Emulation.ExpectedCreateCallback), unitdata_cb := refers(MnccUnitdataCallback) } template BSSAP_Conn_Req ts_BSSAP_Conn_Req(SCCP_PAR_Address peer, SCCP_PAR_Address own, PDU_BSSAP bssap) := { addr_peer := peer, addr_own := own, bssap := bssap }; /* Encode 'l3' and ask BSSMAP_Emulation to create new connection with COMPL L3 INFO */ function f_bssap_compl_l3(PDU_ML3_MS_NW l3) runs on BSC_ConnHdlr { log("Sending COMPL L3: ", l3); var octetstring l3_enc := enc_PDU_ML3_MS_NW(l3); BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_peer, g_pars.sccp_addr_own, valueof(ts_BSSMAP_ComplL3(g_pars.cell_id, l3_enc)))); alt { [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_CONF_IND) {} [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND) { setverdict(fail, "DISC.ind from SCCP"); self.stop; } } } /* helper function to fully establish a dedicated channel */ function f_establish_fully(MobileIdentityLV mi, boolean expect_auth) runs on BSC_ConnHdlr { var PDU_ML3_MS_NW l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, mi)); var PDU_DTAP_MT dtap_mt; /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ f_bssap_compl_l3(l3_info); if (expect_auth) { /* FIXME */ } BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_ACC)); } /* build a PDU_ML3_MS_NW containing a Location Update by IMSI */ function f_build_lu_imsi(hexstring imsi) return PDU_ML3_MS_NW { var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(imsi)); return f_build_lu(mi); } function f_build_lu_imei(hexstring imei) return PDU_ML3_MS_NW { var MobileIdentityLV mi := valueof(ts_MI_IMEI_LV(imei)); return f_build_lu(mi); } function f_build_lu_tmsi(OCT4 tmsi) return PDU_ML3_MS_NW { var MobileIdentityLV mi := valueof(ts_MI_TMSI_LV(tmsi)); return f_build_lu(mi); } private function f_build_lu(MobileIdentityLV mi) return PDU_ML3_MS_NW { var LocationAreaIdentification_V old_lai := { '62F220'O, '9999'O }; var PDU_ML3_MS_NW l3_info := valueof(ts_ML3_MO_LU_Req(valueof(ts_ML3_IE_LuType_Attach), old_lai, mi, valueof(ts_CM1))); return l3_info; } type record AuthVector { OCT16 rand, OCT4 sres, OCT8 kc /* FIXME: 3G elements */ } private function f_rnd_oct(integer len) return octetstring { var integer i; var octetstring res; for (i := 0; i < len; i := i + 1) { res[i] := int2oct(float2int(rnd()*256.0), 1); } return res; } function f_gen_auth_vec_2g() return AuthVector { var AuthVector vec; vec.rand := f_rnd_oct(16); vec.sres := f_rnd_oct(4); vec.kc := f_rnd_oct(8); return vec; } function f_perform_lu(boolean expect_auth, boolean expect_tmsi, boolean send_early_cm, boolean expect_ciph := false) runs on BSC_ConnHdlr { var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi) var PDU_DTAP_MT dtap_mt; var AuthVector vec; /* tell GSUP dispatcher to send this IMSI to us */ f_create_gsup_expect(hex2str(g_pars.imsi)); /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */ f_bssap_compl_l3(l3_lu); if (send_early_cm) { BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3)); } if (expect_auth) { vec := f_gen_auth_vec_2g(); var GSUP_IE auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G(vec.rand, vec.sres, vec.kc)); GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)); GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple)); BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_AUTH_REQ(vec.rand))); BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MT_MM_AUTH_RESP_2G(vec.sres))); } if (expect_ciph) { BSSAP.receive(tr_BSSMAP_CipherModeCmd(?, vec.kc)); g_pars.kc := vec.kc; BSSAP.send(ts_BSSMAP_CipherModeCompl('02'O)); } /* Expect MSC to perform LU with HLR */ GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi)); GSUP.send(ts_GSUP_ISD_REQ(g_pars.imsi, g_pars.msisdn)); GSUP.receive(tr_GSUP_ISD_RES(g_pars.imsi)); GSUP.send(ts_GSUP_UL_RES(g_pars.imsi)); alt { [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Acc)) -> value dtap_mt { var PDU_ML3_LocationUpdateAccept lu_acc := dtap_mt.dtap.msgs.mm.locationUpdateAccept; if (expect_tmsi) { if (not ispresent(lu_acc.mobileIdentityTLV) or not ischosen(lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi)) { setverdict(fail, "Expected TMSI but no TMSI was allocated"); self.stop; } else { g_pars.tmsi := lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi.octets; BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_TmsiRealloc_Cmpl)); } } else { if (ispresent(lu_acc.mobileIdentityTLV) and ischosen(lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi)) { setverdict(fail, "Expected no TMSI but TMSI was allocated"); self.stop; } } } [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)) { setverdict(fail, "Expected LU ACK, but received LU REJ"); self.stop; } } /* FIXME: there could be pending SMS or other common procedures by the MSC, let's ignore them */ BSSAP.receive(tr_BSSMAP_ClearCommand); BSSAP.send(ts_BSSMAP_ClearComplete); BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND); setverdict(pass); } function f_foo() runs on BSC_ConnHdlr{ /* SCCP CC handled by BSSMAP_Emulation_CT.main() */ /* Expect auth, if enabled */ /* TODO: ISD */ /* Expect encr, if enabled */ /* Expect encr, if enabled */ /* Expect ASS CMD, if chan_type != requested */ /* Send ASS CMPL in successful case */ /* Expect AoIP port/ip information for RTP stream */ /* Expect MSC-originated MGCP to our simulated MGW */ /* Verify Counters via CTRL */ /* re-configure MSC behaviour via VTY */ } }