blob: d50c57146bf0e0470c56b885c6d74cb26331b7eb [file] [log] [blame]
Harald Weltea49e36e2018-01-21 19:29:33 +01001module BSC_ConnectionHandler {
2
3import from General_Types all;
4import from Osmocom_Types all;
Harald Welteb71901a2018-01-26 19:16:05 +01005import from Native_Functions all;
Harald Weltea49e36e2018-01-21 19:29:33 +01006import from GSM_Types all;
Harald Welteb71901a2018-01-26 19:16:05 +01007import from IPL4asp_Types all;
Harald Weltea49e36e2018-01-21 19:29:33 +01008import from SCCPasp_Types all;
9import from BSSAP_Types all;
10import from BSSMAP_Emulation all;
11import from BSSMAP_Templates all;
12
13import from GSUP_Types all;
14import from GSUP_Emulation all;
15
16import from MNCC_Types all;
17import from MNCC_Emulation all;
18
Harald Welte4aa970c2018-01-26 10:38:09 +010019import from MGCP_Types all;
20import from MGCP_Emulation all;
Harald Welteb71901a2018-01-26 19:16:05 +010021import from MGCP_Templates all;
22import from SDP_Types all;
Harald Welte4aa970c2018-01-26 10:38:09 +010023
Harald Weltea49e36e2018-01-21 19:29:33 +010024import from MobileL3_Types all;
25import from MobileL3_CommonIE_Types all;
26import from MobileL3_MM_Types all;
Harald Welteb71901a2018-01-26 19:16:05 +010027import from MobileL3_CC_Types all;
Harald Weltea49e36e2018-01-21 19:29:33 +010028import from L3_Templates all;
29
30/* this component represents a single subscriber connection */
Harald Welte4aa970c2018-01-26 10:38:09 +010031type component BSC_ConnHdlr extends BSSAP_ConnHdlr, MNCC_ConnHdlr, GSUP_ConnHdlr, MGCP_ConnHdlr {
Harald Weltea49e36e2018-01-21 19:29:33 +010032 var BSC_ConnHdlrPars g_pars;
Harald Weltea10db902018-01-27 12:44:49 +010033 timer g_Tguard := 60.0;
Harald Weltea49e36e2018-01-21 19:29:33 +010034}
35
Harald Welte148a7082018-01-26 18:56:43 +010036type record AuthVector {
37 OCT16 rand,
38 OCT4 sres,
39 OCT8 kc
40 /* FIXME: 3G elements */
41}
42
Harald Weltede371492018-01-27 23:44:41 +010043type record BSC_ConnHdlrNetworkPars {
44 OCT1 kc_support,
45 boolean expect_tmsi,
46 boolean expect_auth,
47 boolean expect_ciph
48}
49
Harald Weltea49e36e2018-01-21 19:29:33 +010050type record BSC_ConnHdlrPars {
51 SCCP_PAR_Address sccp_addr_own,
52 SCCP_PAR_Address sccp_addr_peer,
53 BSSMAP_IE_CellIdentifier cell_id,
Harald Welte256571e2018-01-24 18:47:19 +010054 hexstring imei,
Harald Weltea49e36e2018-01-21 19:29:33 +010055 hexstring imsi,
Harald Welte82600572018-01-21 20:54:08 +010056 hexstring msisdn,
Harald Welte256571e2018-01-24 18:47:19 +010057 OCT4 tmsi optional,
Harald Welte9de84792018-01-28 01:06:35 +010058 MobileStationClassmark1_V cm1,
Harald Welte82600572018-01-21 20:54:08 +010059 BSSMAP_IE_ClassmarkInformationType2 cm2,
Harald Welte16114282018-01-24 22:41:21 +010060 BSSMAP_IE_ClassmarkInformationType3 cm3 optional,
Harald Weltede371492018-01-27 23:44:41 +010061 AuthVector vec optional,
62 BSC_ConnHdlrNetworkPars net
Harald Weltea49e36e2018-01-21 19:29:33 +010063};
64
Harald Welte9de84792018-01-28 01:06:35 +010065/* get a one-octet bitmaks of supported algorithms based on Classmark information */
66function f_alg_mask_from_cm(BSSMAP_IE_ClassmarkInformationType2 cm2) return OCT1 {
67 var BIT8 res := '00000001'B; /* A5/0 always supported */
68
69 if (cm2.a5_1 == '0'B) {
70 res := res or4b '00000010'B;
71 }
72 if (cm2.classmarkInformationType2_oct5.a5_2 == '1'B ) {
73 res := res or4b '00000100'B;
74 }
75 if (cm2.classmarkInformationType2_oct5.a5_3 == '1'B) {
76 res := res or4b '00001000'B;
77 }
78 /* TODO: CM3 for A5/4 and beyond */
79 return bit2oct(res);
80}
81
82/* determine the best algorithm available within the bit-mask */
83function f_best_alg_from_mask(OCT1 alg_in) return OCT1 {
84 var BIT8 alg := oct2bit(alg_in);
85 var BIT8 ordered_algs[8] := {
86 '10000000'B, '01000000'B, '00100000'B, '00010000'B,
87 '00001000'B, /* A5/3 */
88 '00000010'B, /* A5/1 */
89 '00000100'B, /* A5/2 */
90 '00000001'B /* A5/0 */ }
91 for (var integer i := 0; i < sizeof(ordered_algs); i := i+1) {
92 if (alg and4b ordered_algs[i] != '00000000'B) {
93 return bit2oct(ordered_algs[i]);
94 }
95 }
96 return '00'O;
97}
98
99/* return an integer like '1' for A5/1 based on a mask (with only one bit set */
100function f_alg_from_mask(OCT1 mask_in) return integer {
101 var BIT8 mask := oct2bit(mask_in);
102 for (var integer i := 0; i < 8; i := i+1) {
103 if (mask and4b ('00000001'B << i) != '00000000'B) {
104 return i;
105 }
106 }
107 return -1;
108}
109
Harald Weltea10db902018-01-27 12:44:49 +0100110/* altstep for the global guard timer */
111private altstep as_Tguard() runs on BSC_ConnHdlr {
112 [] g_Tguard.timeout {
Daniel Willmann90829d62018-02-15 17:45:14 +0100113 setverdict(fail, "Tguard timeout");
Harald Weltea10db902018-01-27 12:44:49 +0100114 self.stop;
115 }
116}
117
118/* init function, called as first function in new BSC_ConnHdlr */
Harald Weltead2952e2018-01-27 14:12:46 +0100119function f_init_handler(BSC_ConnHdlrPars pars, float t_guard := 60.0) runs on BSC_ConnHdlr {
Harald Weltea10db902018-01-27 12:44:49 +0100120 /* make parameters available via component variable */
121 g_pars := pars;
122 /* Start guard timer and activate it as default */
Harald Weltead2952e2018-01-27 14:12:46 +0100123 g_Tguard.start(t_guard);
Harald Weltea10db902018-01-27 12:44:49 +0100124 activate(as_Tguard());
125}
126
Harald Weltea49e36e2018-01-21 19:29:33 +0100127
128/* Callback function from general BSSMAP_Emulation whenever a connectionless
129 * BSSMAP message arrives. Canreturn a PDU_BSSAPthat should be sent in return */
130private function BscUnitdataCallback(PDU_BSSAP bssap)
131runs on BSSMAP_Emulation_CT return template PDU_BSSAP {
132 var template PDU_BSSAP resp := omit;
133
134 log("BSSMAP_BscUnitdataCallback");
135 /* answer all RESET with RESET ACK */
136 if (match(bssap, tr_BSSMAP_Reset)){
137 log("BSSMAP_BscUnitdataCallback: Responding to RESET with RESET-ACK");
138 resp := ts_BSSMAP_ResetAck;
139 }
140
141 /* FIXME: Handle paging, etc. */
142 return resp;
143}
144
145const BssmapOps BSC_BssmapOps := {
146 /* Create call-back for inbound connections from MSC (hand-over) */
147 create_cb := refers(BSSMAP_Emulation.ExpectedCreateCallback),
148 unitdata_cb := refers(BscUnitdataCallback),
149 decode_dtap := true,
Harald Weltea4ca4462018-02-09 00:17:14 +0100150 role_ms := true,
151 sccp_addr_local := omit,
152 sccp_addr_peer := omit
Harald Weltea49e36e2018-01-21 19:29:33 +0100153}
154
155
156private function MnccUnitdataCallback(MNCC_PDU mncc)
157runs on MNCC_Emulation_CT return template MNCC_PDU {
158 log("Ignoring MNCC", mncc);
159 return omit;
160}
161
162const MnccOps BCC_MnccOps := {
163 create_cb := refers(MNCC_Emulation.ExpectedCreateCallback),
164 unitdata_cb := refers(MnccUnitdataCallback)
165}
166
167
168
169template BSSAP_Conn_Req ts_BSSAP_Conn_Req(SCCP_PAR_Address peer, SCCP_PAR_Address own, PDU_BSSAP bssap) := {
170 addr_peer := peer,
171 addr_own := own,
172 bssap := bssap
173};
174
Harald Weltea49e36e2018-01-21 19:29:33 +0100175/* Encode 'l3' and ask BSSMAP_Emulation to create new connection with COMPL L3 INFO */
176function f_bssap_compl_l3(PDU_ML3_MS_NW l3)
177runs on BSC_ConnHdlr {
178 log("Sending COMPL L3: ", l3);
179 var octetstring l3_enc := enc_PDU_ML3_MS_NW(l3);
180 BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_peer, g_pars.sccp_addr_own,
181 valueof(ts_BSSMAP_ComplL3(g_pars.cell_id, l3_enc))));
Harald Welte71b69332018-01-21 20:43:53 +0100182 alt {
183 [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_CONF_IND) {}
184 [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {
185 setverdict(fail, "DISC.ind from SCCP");
186 self.stop;
187 }
188 }
Harald Weltea49e36e2018-01-21 19:29:33 +0100189}
190
Harald Welte081b19a2018-02-10 09:11:13 +0100191type enumerated EstablishType {
192 EST_TYPE_MO_CALL,
Harald Welte0bef21e2018-02-10 09:48:23 +0100193 EST_TYPE_EMERG_CALL,
Harald Welte081b19a2018-02-10 09:11:13 +0100194 EST_TYPE_PAG_RESP
195};
196
Harald Weltea49e36e2018-01-21 19:29:33 +0100197/* helper function to fully establish a dedicated channel */
Harald Welte081b19a2018-02-10 09:11:13 +0100198function f_establish_fully(MobileIdentityLV mi, EstablishType etype := EST_TYPE_MO_CALL)
Harald Weltea49e36e2018-01-21 19:29:33 +0100199runs on BSC_ConnHdlr {
Harald Welte081b19a2018-02-10 09:11:13 +0100200 var PDU_ML3_MS_NW l3_info;
201 select (etype) {
202 case (EST_TYPE_MO_CALL) {
203 l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, mi));
204 }
Harald Welte0bef21e2018-02-10 09:48:23 +0100205 case (EST_TYPE_EMERG_CALL) {
206 l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_EMERG_CALL, mi));
207 }
Harald Welte081b19a2018-02-10 09:11:13 +0100208 case (EST_TYPE_PAG_RESP) {
209 l3_info := valueof(ts_PAG_RESP(mi));
210 }
211 }
Harald Weltea49e36e2018-01-21 19:29:33 +0100212
213 /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */
214 f_bssap_compl_l3(l3_info);
215
Harald Weltede371492018-01-27 23:44:41 +0100216 f_mm_common();
217 if (g_pars.net.expect_ciph) {
Harald Welte148a7082018-01-26 18:56:43 +0100218 /* implicit CM SERVICE ACCEPT? */
219 } else {
Harald Welte0bef21e2018-02-10 09:48:23 +0100220 if (etype != EST_TYPE_PAG_RESP) {
Harald Welte081b19a2018-02-10 09:11:13 +0100221 /* explicit CM SERVICE ACCEPT */
222 BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_ACC));
223 }
Harald Weltea49e36e2018-01-21 19:29:33 +0100224 }
Harald Weltea49e36e2018-01-21 19:29:33 +0100225}
226
227/* build a PDU_ML3_MS_NW containing a Location Update by IMSI */
Harald Welte9de84792018-01-28 01:06:35 +0100228function f_build_lu_imsi(hexstring imsi) runs on BSC_ConnHdlr return PDU_ML3_MS_NW
Harald Weltea49e36e2018-01-21 19:29:33 +0100229{
230 var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(imsi));
231 return f_build_lu(mi);
232}
Harald Welte9de84792018-01-28 01:06:35 +0100233function f_build_lu_imei(hexstring imei) runs on BSC_ConnHdlr return PDU_ML3_MS_NW
Harald Welteba7b6d92018-01-23 21:32:34 +0100234{
235 var MobileIdentityLV mi := valueof(ts_MI_IMEI_LV(imei));
236 return f_build_lu(mi);
237}
Harald Welte9de84792018-01-28 01:06:35 +0100238function f_build_lu_tmsi(OCT4 tmsi) runs on BSC_ConnHdlr return PDU_ML3_MS_NW
Harald Welteba7b6d92018-01-23 21:32:34 +0100239{
240 var MobileIdentityLV mi := valueof(ts_MI_TMSI_LV(tmsi));
241 return f_build_lu(mi);
242}
Harald Welte9de84792018-01-28 01:06:35 +0100243private function f_build_lu(MobileIdentityLV mi) runs on BSC_ConnHdlr return PDU_ML3_MS_NW
Harald Weltea49e36e2018-01-21 19:29:33 +0100244{
245 var LocationAreaIdentification_V old_lai := { '62F220'O, '9999'O };
246 var PDU_ML3_MS_NW l3_info := valueof(ts_ML3_MO_LU_Req(valueof(ts_ML3_IE_LuType_Attach),
Harald Welte9de84792018-01-28 01:06:35 +0100247 old_lai, mi, g_pars.cm1));
Harald Weltea49e36e2018-01-21 19:29:33 +0100248 return l3_info;
249}
250
Harald Weltecf66d5a2018-01-23 19:24:28 +0100251private function f_rnd_oct(integer len) return octetstring {
252 var integer i;
253 var octetstring res;
254 for (i := 0; i < len; i := i + 1) {
255 res[i] := int2oct(float2int(rnd()*256.0), 1);
256 }
257 return res;
258}
259
260function f_gen_auth_vec_2g() return AuthVector {
261 var AuthVector vec;
262 vec.rand := f_rnd_oct(16);
263 vec.sres := f_rnd_oct(4);
264 vec.kc := f_rnd_oct(8);
265 return vec;
266}
267
Harald Welte148a7082018-01-26 18:56:43 +0100268
Harald Welte9de84792018-01-28 01:06:35 +0100269function f_mm_auth() runs on BSC_ConnHdlr
Harald Welte148a7082018-01-26 18:56:43 +0100270{
Harald Weltede371492018-01-27 23:44:41 +0100271 if (g_pars.net.expect_auth) {
Harald Welte148a7082018-01-26 18:56:43 +0100272 g_pars.vec := f_gen_auth_vec_2g();
273 var GSUP_IE auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G(g_pars.vec.rand,
274 g_pars.vec.sres,
275 g_pars.vec.kc));
276 GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi));
277 GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple));
278
279 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_AUTH_REQ(g_pars.vec.rand)));
280 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MT_MM_AUTH_RESP_2G(g_pars.vec.sres)));
281 }
Harald Welte9de84792018-01-28 01:06:35 +0100282}
Harald Welte148a7082018-01-26 18:56:43 +0100283
Harald Welte9de84792018-01-28 01:06:35 +0100284function f_mm_common() runs on BSC_ConnHdlr
285{
286 f_mm_auth();
Harald Weltede371492018-01-27 23:44:41 +0100287 if (g_pars.net.expect_ciph) {
Harald Welte9de84792018-01-28 01:06:35 +0100288 var OCT1 a5_net := f_alg_mask_from_cm(g_pars.cm2);
289 var OCT1 a5_intersect := g_pars.net.kc_support and4b a5_net;
290 alt {
291 [] BSSAP.receive(tr_BSSMAP_CipherModeCmd(a5_intersect, g_pars.vec.kc)) {
292 var OCT1 a5_chosen := f_best_alg_from_mask(a5_intersect);
293 var integer a5_nr := f_alg_from_mask(a5_chosen);
294 BSSAP.send(ts_BSSMAP_CipherModeCompl(int2oct(a5_nr+1, 1)));
295 }
296 [] BSSAP.receive(tr_BSSMAP_CipherModeCmd(?, g_pars.vec.kc)) {
297 setverdict(fail, "Wrong ciphering algorithm mask in CiphModCmd");
298 self.stop;
299 }
300 }
301 /* FIXME: Send the best available algorithm */
Harald Welte148a7082018-01-26 18:56:43 +0100302 }
303}
304
Harald Weltede371492018-01-27 23:44:41 +0100305function f_perform_lu(boolean send_early_cm)
Harald Weltea49e36e2018-01-21 19:29:33 +0100306runs on BSC_ConnHdlr {
307 var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi)
308 var PDU_DTAP_MT dtap_mt;
309
310 /* tell GSUP dispatcher to send this IMSI to us */
311 f_create_gsup_expect(hex2str(g_pars.imsi));
312
313 /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */
314 f_bssap_compl_l3(l3_lu);
315
Harald Welte8a121b32018-01-22 03:00:41 +0100316 if (send_early_cm) {
317 BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3));
318 }
Harald Welte5c2622c2018-01-21 20:45:20 +0100319
Harald Weltede371492018-01-27 23:44:41 +0100320 f_mm_common();
Harald Welte16114282018-01-24 22:41:21 +0100321
Harald Weltea49e36e2018-01-21 19:29:33 +0100322 /* Expect MSC to perform LU with HLR */
323 GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi));
324 GSUP.send(ts_GSUP_ISD_REQ(g_pars.imsi, g_pars.msisdn));
325 GSUP.receive(tr_GSUP_ISD_RES(g_pars.imsi));
326 GSUP.send(ts_GSUP_UL_RES(g_pars.imsi));
327
328 alt {
329 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Acc)) -> value dtap_mt {
330 var PDU_ML3_LocationUpdateAccept lu_acc := dtap_mt.dtap.msgs.mm.locationUpdateAccept;
Harald Weltede371492018-01-27 23:44:41 +0100331 if (g_pars.net.expect_tmsi) {
Harald Weltea49e36e2018-01-21 19:29:33 +0100332 if (not ispresent(lu_acc.mobileIdentityTLV) or
333 not ischosen(lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi)) {
334 setverdict(fail, "Expected TMSI but no TMSI was allocated");
335 self.stop;
336 } else {
Harald Welte256571e2018-01-24 18:47:19 +0100337 g_pars.tmsi := lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi.octets;
Harald Weltea49e36e2018-01-21 19:29:33 +0100338 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_TmsiRealloc_Cmpl));
339 }
340 } else {
341 if (ispresent(lu_acc.mobileIdentityTLV) and
342 ischosen(lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi)) {
343 setverdict(fail, "Expected no TMSI but TMSI was allocated");
344 self.stop;
345 }
346 }
347 }
348 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)) {
349 setverdict(fail, "Expected LU ACK, but received LU REJ");
350 self.stop;
351 }
352 }
353 /* FIXME: there could be pending SMS or other common procedures by the MSC, let's ignore them */
354 BSSAP.receive(tr_BSSMAP_ClearCommand);
355 BSSAP.send(ts_BSSMAP_ClearComplete);
356 BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND);
357 setverdict(pass);
358}
359
360function f_foo() runs on BSC_ConnHdlr{
361 /* SCCP CC handled by BSSMAP_Emulation_CT.main() */
362 /* Expect auth, if enabled */
363
364 /* TODO: ISD */
365 /* Expect encr, if enabled */
366 /* Expect encr, if enabled */
367 /* Expect ASS CMD, if chan_type != requested */
368 /* Send ASS CMPL in successful case */
369
370 /* Expect AoIP port/ip information for RTP stream */
371 /* Expect MSC-originated MGCP to our simulated MGW */
372 /* Verify Counters via CTRL */
373 /* re-configure MSC behaviour via VTY */
374}
375
Harald Welteb71901a2018-01-26 19:16:05 +0100376/* parameters related to a (MO?) voice call */
377type record CallParameters {
Harald Welteb71901a2018-01-26 19:16:05 +0100378 /* CC related parameters */
379 hexstring called_party, /* whom are we calling */
380 integer transaction_id optional, /* which TS 04.08 CC transaction ID to use */
381 BearerCapability_TLV bearer_cap, /* which bearer capabilities to claim */
Harald Welte0bef21e2018-02-10 09:48:23 +0100382 boolean emergency, /* is this an emergency call? */
Harald Welteb71901a2018-01-26 19:16:05 +0100383
384 /* MNCC related parameters */
385 uint32_t mncc_callref optional, /* call reference on the MNCC side */
386 MNCC_bearer_cap mncc_bearer_cap optional, /* MNCC-side bearer capabilities */
387
388 /* RTP related parameters */
389 HostName bss_rtp_ip optional, /* BSS Side RTP IP */
390 PortNumber bss_rtp_port optional, /* BSS Side RTP Port */
391 HostName mss_rtp_ip optional, /* MSS Side RTP IP */
392 PortNumber mss_rtp_port optional, /* MSS Side RTP Port */
Harald Welte4017d552018-01-26 21:40:05 +0100393 HostName mgw_rtp_ip_bss, /* BSS-facing MGW RTP IP */
394 PortNumber mgw_rtp_port_bss, /* BSS-facing MGW RTP Port */
395 HostName mgw_rtp_ip_mss, /* MSS-facing MGW RTP IP */
396 PortNumber mgw_rtp_port_mss, /* MSS-facing MGW RTP Port */
Harald Welteb71901a2018-01-26 19:16:05 +0100397 uint7_t rtp_payload_type, /* dynamic RTP payload type */
398 charstring rtp_sdp_format, /* AMR/8000 or the like */
399
400 MgcpCallId mgcp_call_id optional, /* MGCP Call ID; CallAgent allocated */
401 MgcpEndpoint mgcp_ep optional /* MGCP Endpoint, CallAgent or MGW allocated */,
402 MgcpConnectionId mgcp_connection_id_bss, /* MGCP Connection ID BSS Side */
403 MgcpConnectionId mgcp_connection_id_mss /* MGCP Connection ID MSS Side */
404}
405
406template (value) CallParameters t_CallParams(hexstring called, integer tid) := {
Harald Welteb71901a2018-01-26 19:16:05 +0100407 called_party := called,
408 transaction_id := tid,
409 bearer_cap := valueof(ts_Bcap_voice),
Harald Welte0bef21e2018-02-10 09:48:23 +0100410 emergency := false,
Harald Welteb71901a2018-01-26 19:16:05 +0100411 mncc_callref := omit,
412 mncc_bearer_cap := valueof(ts_MNCC_bcap_voice),
Harald Welte4017d552018-01-26 21:40:05 +0100413 bss_rtp_ip := "9.8.7.6",
414 bss_rtp_port := 9000,
Harald Welteb71901a2018-01-26 19:16:05 +0100415 mss_rtp_ip := omit,
416 mss_rtp_port := omit,
Harald Welte4017d552018-01-26 21:40:05 +0100417 mgw_rtp_ip_bss := "1.1.1.1",
418 mgw_rtp_port_bss := 10000,
419 mgw_rtp_ip_mss := "1.1.1.1",
420 mgw_rtp_port_mss := 11000,
Harald Welteb71901a2018-01-26 19:16:05 +0100421 rtp_payload_type := 98,
422 rtp_sdp_format := "AMR/8000",
423 mgcp_call_id := omit,
424 mgcp_ep := omit,
425 mgcp_connection_id_bss := '0'H,//
426 mgcp_connection_id_mss := '0'H //
427};
428
429
Harald Welte33ec09b2018-02-10 15:34:46 +0100430function f_mt_call(inout CallParameters cpars)
431runs on BSC_ConnHdlr {
432
433 var MobileIdentityLV mi;
434 var MNCC_PDU mncc;
435 var MgcpCommand mgcp_cmd;
436
437 f_bssmap_register_imsi(g_pars.imsi, g_pars.tmsi);
438
439 /* Allocate a call reference and send SETUP via MNCC to MSC */
440 cpars.mncc_callref := f_rnd_int(2147483648);
441 MNCC.send(ts_MNCC_SETUP_req(cpars.mncc_callref, hex2str(g_pars.msisdn),
442 hex2str(cpars.called_party), hex2str(g_pars.imsi)));
443 /* BSC <- MSC: Expect paging. FIXME: By TMSI or not? */
444 BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi));
445
446 /* If we have a TMSI, use TMSI instead of IMSI */
447 if (ispresent(g_pars.tmsi)) {
448 mi := valueof(ts_MI_TMSI_LV(g_pars.tmsi));
449 } else {
450 mi := valueof(ts_MI_IMSI_LV(g_pars.imsi));
451 }
452 f_establish_fully(mi, EST_TYPE_PAG_RESP);
453
454 /* MS <- MSC: Expect CC SETUP */
455 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_SETUP(cpars.transaction_id, *, cpars.called_party)));
456
457 /* MS -> MSC: ALERTING */
458 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_ALERTING(cpars.transaction_id)));
459 MNCC.receive(tr_MNCC_ALERT_ind(cpars.mncc_callref));
460
461
462 /* Create MGCP expect */
463 f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit});
464 /* Ask MSC via MNCC to create the RTP socket on the MSC/MGW side */
465 MNCC.send(ts_MNCC_RTP_CREATE(cpars.mncc_callref));
466
467 /* First MGCP CRCX (for BSS/RAN side) */
468 MGCP.receive(tr_CRCX) -> value mgcp_cmd {
469 cpars.mgcp_call_id := f_MgcpCmd_extract_call_id(mgcp_cmd);
470 /* TODO: dynamic EP allocation case */
471 cpars.mgcp_ep := mgcp_cmd.line.ep;
472 var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_rtp_ip_bss, cpars.mgw_rtp_ip_bss,
473 hex2str(cpars.mgcp_call_id), "42",
474 cpars.mgw_rtp_port_bss,
475 { int2str(cpars.rtp_payload_type) },
476 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
477 cpars.rtp_sdp_format)),
478 valueof(ts_SDP_ptime(20)) }));
479 MGCP.send(ts_CRCX_ACK(mgcp_cmd.line.trans_id, cpars.mgcp_connection_id_bss, sdp));
480 }
481 /* Second MGCP CRCX (this time for MSS/CN side) */
482 MGCP.receive(tr_CRCX(cpars.mgcp_ep)) -> value mgcp_cmd {
483 var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_rtp_ip_mss, cpars.mgw_rtp_ip_mss,
484 hex2str(cpars.mgcp_call_id), "42",
485 cpars.mgw_rtp_port_mss,
486 { int2str(cpars.rtp_payload_type) },
487 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
488 cpars.rtp_sdp_format)),
489 valueof(ts_SDP_ptime(20)) }));
490 MGCP.send(ts_CRCX_ACK(mgcp_cmd.line.trans_id, cpars.mgcp_connection_id_mss, sdp));
491 /* MSC acknowledges the MNCC_CREATE to the MNCC handler */
492 MNCC.receive(tr_MNCC_RTP_CREATE(cpars.mncc_callref));
493 }
494
495 /* expect the MSC to trigger a BSSMAP ASSIGNMENT */
496 var BSSMAP_IE_AoIP_TransportLayerAddress tla_ass :=
497 valueof(ts_BSSMAP_IE_AoIP_TLA4(f_inet_addr(cpars.mgw_rtp_ip_bss),cpars.mgw_rtp_port_bss));
498 BSSAP.receive(tr_BSSMAP_AssignmentReq(omit, tla_ass)) {
499 var BSSMAP_IE_AoIP_TransportLayerAddress tla;
500 tla := valueof(ts_BSSMAP_IE_AoIP_TLA4(f_inet_addr(cpars.bss_rtp_ip), cpars.bss_rtp_port));
501 BSSAP.send(ts_BSSMAP_AssignmentComplete(omit, tla));
502 }
503
504 /* MS -> MSC: ALERTING */
505 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_CONNECT(cpars.transaction_id)));
506 MNCC.receive(tr_MNCC_SETUP_cnf(cpars.mncc_callref));
507
508 /* FIXME */
509 f_sleep(3.0);
510
511 /* Hangup by "A" side */
512 MNCC.send(ts_MNCC_DISC_req(cpars.mncc_callref, valueof(ts_MNCC_cause(23))));
513 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_DISC(cpars.transaction_id)));
514
515 if (false) {
516 /* A-side (PLMN) Release of call */
517 MNCC.send(ts_MNCC_REL_req(cpars.mncc_callref, valueof(ts_MNCC_cause(42))));
518 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_RELEASE(cpars.transaction_id)));
519 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_REL_COMPL(cpars.transaction_id)));
520 } else {
521 /* B-side (MS) Release of call */
522 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_RELEASE(cpars.transaction_id, '1'B, '0000000'B)));
523 MNCC.receive(tr_MNCC_REL_ind(cpars.mncc_callref));
524 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_REL_COMPL(cpars.transaction_id)));
525 }
526
527 /* clearing of radio channel */
528 interleave {
529 [] BSSAP.receive(tr_BSSMAP_ClearCommand) {
530 BSSAP.send(ts_BSSMAP_ClearComplete);
531 BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND);
532 }
533 [] MGCP.receive(tr_DLCX(?)) -> value mgcp_cmd {
534 /* TODO: For one or all connections on EP? */
535 MGCP.send(ts_DLCX_ACK2(mgcp_cmd.line.trans_id));
536 f_create_mgcp_delete_ep(cpars.mgcp_ep);
537 }
538 }
539 setverdict(pass);
540}
541
Harald Welteb71901a2018-01-26 19:16:05 +0100542function f_mo_call(inout CallParameters cpars)
543runs on BSC_ConnHdlr {
544
545 var MobileIdentityLV mi;
546 var MNCC_PDU mncc;
547 var MgcpCommand mgcp_cmd;
548
549 /* If we have a TMSI, use TMSI instead of IMSI */
550 if (ispresent(g_pars.tmsi)) {
551 mi := valueof(ts_MI_TMSI_LV(g_pars.tmsi));
552 } else {
553 mi := valueof(ts_MI_IMSI_LV(g_pars.imsi));
554 }
Harald Welte0bef21e2018-02-10 09:48:23 +0100555 if (cpars.emergency) {
556 f_establish_fully(mi, EST_TYPE_EMERG_CALL);
557 } else {
558 f_establish_fully(mi, EST_TYPE_MO_CALL);
559 }
Harald Welteb71901a2018-01-26 19:16:05 +0100560
561 /* Create MNCC and MGCP expect */
562 f_create_mncc_expect(hex2str(cpars.called_party));
563 f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit});
564
Harald Welte0bef21e2018-02-10 09:48:23 +0100565 if (cpars.emergency) {
566 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_EMERG_SETUP(cpars.transaction_id)));
567 } else {
568 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_SETUP(cpars.transaction_id, cpars.called_party)));
569 }
Harald Welteb71901a2018-01-26 19:16:05 +0100570 interleave {
571 [] MNCC.receive(tr_MNCC_SETUP_ind(?, tr_MNCC_number(hex2str(cpars.called_party)))) -> value mncc {
572 cpars.mncc_callref := mncc.u.signal.callref;
573 /* Call Proceeding */
574 MNCC.send(ts_MNCC_CALL_PROC_req(cpars.mncc_callref, cpars.mncc_bearer_cap));
575 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_CALL_PROC(cpars.transaction_id)));
576 };
Harald Welte4017d552018-01-26 21:40:05 +0100577 /* First MGCP CRCX (for BSS/RAN side) */
Harald Welteb71901a2018-01-26 19:16:05 +0100578 [] MGCP.receive(tr_CRCX) -> value mgcp_cmd {
579 cpars.mgcp_call_id := f_MgcpCmd_extract_call_id(mgcp_cmd);
580 /* TODO: dynamic EP allocation case */
581 cpars.mgcp_ep := mgcp_cmd.line.ep;
Harald Welte4017d552018-01-26 21:40:05 +0100582 var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_rtp_ip_bss, cpars.mgw_rtp_ip_bss,
Harald Welteb71901a2018-01-26 19:16:05 +0100583 hex2str(cpars.mgcp_call_id), "42",
Harald Welte4017d552018-01-26 21:40:05 +0100584 cpars.mgw_rtp_port_bss,
Harald Welteb71901a2018-01-26 19:16:05 +0100585 { int2str(cpars.rtp_payload_type) },
586 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
587 cpars.rtp_sdp_format)),
588 valueof(ts_SDP_ptime(20)) }));
589 MGCP.send(ts_CRCX_ACK(mgcp_cmd.line.trans_id, cpars.mgcp_connection_id_bss, sdp));
590 }
591 }
Harald Welte4017d552018-01-26 21:40:05 +0100592 /* Second MGCP CRCX (this time for MSS/CN side) */
593 MGCP.receive(tr_CRCX(cpars.mgcp_ep)) -> value mgcp_cmd {
594 var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_rtp_ip_mss, cpars.mgw_rtp_ip_mss,
595 hex2str(cpars.mgcp_call_id), "42",
596 cpars.mgw_rtp_port_mss,
597 { int2str(cpars.rtp_payload_type) },
598 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
599 cpars.rtp_sdp_format)),
600 valueof(ts_SDP_ptime(20)) }));
601 MGCP.send(ts_CRCX_ACK(mgcp_cmd.line.trans_id, cpars.mgcp_connection_id_mss, sdp));
602 }
Harald Welteb71901a2018-01-26 19:16:05 +0100603
604 /* Alerting */
605 MNCC.send(ts_MNCC_ALERT_req(cpars.mncc_callref));
Harald Welteb71901a2018-01-26 19:16:05 +0100606
Harald Welte4017d552018-01-26 21:40:05 +0100607 var BSSMAP_IE_AoIP_TransportLayerAddress tla_ass :=
608 valueof(ts_BSSMAP_IE_AoIP_TLA4(f_inet_addr(cpars.mgw_rtp_ip_bss),cpars.mgw_rtp_port_bss));
609 interleave {
610 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_ALERTING(cpars.transaction_id))) {}
611 /* expect AoIP IP/Port to match what we returned in CRCX_ACK above */
612 [] BSSAP.receive(tr_BSSMAP_AssignmentReq(omit, tla_ass)) {
Harald Welteb71901a2018-01-26 19:16:05 +0100613 var BSSMAP_IE_AoIP_TransportLayerAddress tla;
614 tla := valueof(ts_BSSMAP_IE_AoIP_TLA4(f_inet_addr(cpars.bss_rtp_ip), cpars.bss_rtp_port));
615 BSSAP.send(ts_BSSMAP_AssignmentComplete(omit, tla));
616 }
617 }
618
Harald Welte4017d552018-01-26 21:40:05 +0100619 /* Answer. MNCC_SETUP_RSP -> CONNECT to MS; CONNECT_ACK from MS */
620 MNCC.send(ts_MNCC_SETUP_rsp(cpars.mncc_callref));
621 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_CONNECT(cpars.transaction_id)));
622 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_CONNECT_ACK(cpars.transaction_id)));
Harald Welteb71901a2018-01-26 19:16:05 +0100623
624 f_sleep(3.0);
625
626 /* Hangup by "B" side */
627 MNCC.send(ts_MNCC_DISC_req(cpars.mncc_callref, valueof(ts_MNCC_cause(23))));
628 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_DISC(cpars.transaction_id)));
629
630 /* Release of call */
631 MNCC.send(ts_MNCC_REL_req(cpars.mncc_callref, valueof(ts_MNCC_cause(42))));
632 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_RELEASE(cpars.transaction_id)));
633 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_REL_COMPL(cpars.transaction_id)));
634
635 /* clearing of radio channel */
636 interleave {
637 [] BSSAP.receive(tr_BSSMAP_ClearCommand) {
638 BSSAP.send(ts_BSSMAP_ClearComplete);
639 BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND);
640 }
641 [] MGCP.receive(tr_DLCX(?)) -> value mgcp_cmd {
642 /* TODO: For one or all connections on EP? */
643 MGCP.send(ts_DLCX_ACK2(mgcp_cmd.line.trans_id));
644 f_create_mgcp_delete_ep(cpars.mgcp_ep);
645 }
646 }
647 setverdict(pass);
648}
Harald Weltea49e36e2018-01-21 19:29:33 +0100649
Daniel Willmann8b084372018-02-04 13:35:26 +0100650function f_mo_seq_dtmf_dup(inout CallParameters cpars)
651runs on BSC_ConnHdlr {
652
653 timer T := 1.0;
654 var MobileIdentityLV mi;
655 var MNCC_PDU mncc;
656 var MgcpCommand mgcp_cmd;
657 var template PDU_ML3_MS_NW dtmf_dtap;
658
659 /* If we have a TMSI, use TMSI instead of IMSI */
660 if (ispresent(g_pars.tmsi)) {
661 mi := valueof(ts_MI_TMSI_LV(g_pars.tmsi));
662 } else {
663 mi := valueof(ts_MI_IMSI_LV(g_pars.imsi));
664 }
665 f_establish_fully(mi);
666
667 /* Create MNCC and MGCP expect */
668 f_create_mncc_expect(hex2str(cpars.called_party));
669 f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit});
670
671 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_SETUP(cpars.transaction_id, cpars.called_party)));
672 MNCC.receive(tr_MNCC_SETUP_ind(?, tr_MNCC_number(hex2str(cpars.called_party)))) -> value mncc;
673 cpars.mncc_callref := mncc.u.signal.callref;
674
675 /* Send DTMF */
676 dtmf_dtap := ts_ML3_MO_CC_START_DTMF(cpars.transaction_id, "2");
677 dtmf_dtap.msgs.cc.startDTMF.nsd := int2bit(2, 2);
Daniel Willmann92f66272018-02-06 15:50:52 +0100678 BSSAP.send(ts_PDU_DTAP_MO(dtmf_dtap, '00'O, true));
Daniel Willmann8b084372018-02-04 13:35:26 +0100679 T.start;
680 alt {
681 [] MNCC.receive(tr_MNCC_START_DTMF_ind(cpars.mncc_callref, "2")) {}
682 [] T.timeout {
683 setverdict(fail, "Timeout waiting for START_DTMF_ind");
684 self.stop;
685 }
686 }
687
Daniel Willmann92f66272018-02-06 15:50:52 +0100688 BSSAP.send(ts_PDU_DTAP_MO(dtmf_dtap, '00'O, true));
Daniel Willmann8b084372018-02-04 13:35:26 +0100689 T.start;
690 alt {
691 [] MNCC.receive(tr_MNCC_START_DTMF_ind(cpars.mncc_callref, "2")) {
692 setverdict(fail, "Received duplicate START_DTMF_ind");
693 self.stop;
694 }
695 [] T.timeout { }
696 }
697
698 dtmf_dtap := ts_ML3_MO_CC_START_DTMF(cpars.transaction_id, "3");
699 dtmf_dtap.msgs.cc.startDTMF.nsd := int2bit(3, 2);
Daniel Willmann92f66272018-02-06 15:50:52 +0100700 BSSAP.send(ts_PDU_DTAP_MO(dtmf_dtap, '00'O, true));
Daniel Willmann8b084372018-02-04 13:35:26 +0100701 alt {
702 [] MNCC.receive(tr_MNCC_START_DTMF_ind(cpars.mncc_callref, "3")) { }
703 [] T.timeout {
704 setverdict(fail, "Received duplicate START_DTMF_ind");
705 self.stop;
706 }
707 }
708
709 setverdict(pass);
710}
Harald Welte1ddc7162018-01-27 14:25:46 +0100711/* expect a clear command */
712function f_expect_clear(float t := 5.0) runs on BSC_ConnHdlr {
713 timer T := t;
714
715 T.start;
716 alt {
717 [] BSSAP.receive(tr_BSSMAP_ClearCommand) { }
718 [] BSSAP.receive {
719 setverdict(fail, "Unexpected BSSMAP while waiting for ClearCommand");
720 self.stop;
721 }
722 [] T.timeout {
Daniel Willmann90829d62018-02-15 17:45:14 +0100723 setverdict(fail, "Timeout waiting for ClearCommand");
Harald Welte1ddc7162018-01-27 14:25:46 +0100724 self.stop;
725 }
726 }
727
728 BSSAP.send(ts_BSSMAP_ClearComplete);
729
730 T.start;
731 alt {
732 [] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {
733 setverdict(pass);
734 }
735 [] BSSAP.receive {
736 setverdict(fail, "Unexpected BSSMAP while waiting for SCCP Release");
737 self.stop;
738 }
739 [] T.timeout {
Daniel Willmann90829d62018-02-15 17:45:14 +0100740 setverdict(fail, "Timeout waiting for SCCP Release");
Harald Welte1ddc7162018-01-27 14:25:46 +0100741 self.stop;
742 }
743 }
744}
745
Harald Weltea49e36e2018-01-21 19:29:33 +0100746
747
748
749}
750
751