blob: 30adb03f0b72634ab05c28f9b007fece8875b5ec [file] [log] [blame]
Harald Welte34b5a952019-05-27 11:54:11 +02001/* BSC Connection Handler of MSC test suite in TTCN-3
2 * (C) 2018-2019 Harald Welte <laforge@gnumonks.org>
3 * (C) 2018-2019 sysmocom - s.f.m.c. GmbH
4 * (C) 2018 Vadim Yanitskiy <axilirator@gmail.com>
5 * All rights reserved.
6 *
7 * Released under the terms of GNU General Public License, Version 2 or
8 * (at your option) any later version.
9 *
10 * SPDX-License-Identifier: GPL-2.0-or-later
11 */
12
Harald Weltea49e36e2018-01-21 19:29:33 +010013module BSC_ConnectionHandler {
14
15import from General_Types all;
16import from Osmocom_Types all;
Harald Welteb71901a2018-01-26 19:16:05 +010017import from Native_Functions all;
Pau Espin Pedrol563b3d02020-09-09 20:19:52 +020018import from Misc_Helpers all;
Harald Weltea49e36e2018-01-21 19:29:33 +010019import from GSM_Types all;
Harald Welteb71901a2018-01-26 19:16:05 +010020import from IPL4asp_Types all;
Harald Weltea49e36e2018-01-21 19:29:33 +010021import from SCCPasp_Types all;
22import from BSSAP_Types all;
Harald Welte6811d102019-04-14 22:23:14 +020023import from RAN_Emulation all;
Harald Weltea49e36e2018-01-21 19:29:33 +010024import from BSSMAP_Templates all;
25
Harald Welte9b751a62019-04-14 17:39:29 +020026import from RANAP_Constants all;
27import from RANAP_IEs all;
28import from RANAP_PDU_Contents all;
29import from RANAP_PDU_Descriptions all;
30import from RANAP_Templates all;
31
Harald Weltea49e36e2018-01-21 19:29:33 +010032import from GSUP_Types all;
33import from GSUP_Emulation all;
34
35import from MNCC_Types all;
36import from MNCC_Emulation all;
37
Harald Welte4aa970c2018-01-26 10:38:09 +010038import from MGCP_Types all;
39import from MGCP_Emulation all;
Harald Welteb71901a2018-01-26 19:16:05 +010040import from MGCP_Templates all;
41import from SDP_Types all;
Harald Welte4aa970c2018-01-26 10:38:09 +010042
Harald Weltea49e36e2018-01-21 19:29:33 +010043import from MobileL3_Types all;
44import from MobileL3_CommonIE_Types all;
45import from MobileL3_MM_Types all;
Harald Welteb71901a2018-01-26 19:16:05 +010046import from MobileL3_CC_Types all;
Harald Weltef45efeb2018-04-09 18:19:24 +020047import from MobileL3_SMS_Types all;
Harald Weltea49e36e2018-01-21 19:29:33 +010048import from L3_Templates all;
Harald Welte158a7ca2018-02-16 18:11:31 +010049import from L3_Common all;
Harald Weltea49e36e2018-01-21 19:29:33 +010050
Harald Weltef640a012018-04-14 17:49:21 +020051import from SMPP_Emulation all;
52
Philipp Maieraeb29a82018-11-08 17:40:53 +010053import from IPA_Emulation all;
54import from Osmocom_CTRL_Functions all;
55import from Osmocom_CTRL_Types all;
56import from Osmocom_CTRL_Adapter all;
57
Philipp Maierc39a9d82018-11-09 11:21:08 +010058import from TELNETasp_PortType all;
59import from Osmocom_VTY_Functions all;
60
Harald Welte4263c522018-12-06 11:56:27 +010061import from SGsAP_Emulation all;
62
Neels Hofmeyr4e18ccc2020-06-24 19:08:17 +020063import from TCCConversion_Functions { function f_strstr };
64
Harald Weltea49e36e2018-01-21 19:29:33 +010065/* this component represents a single subscriber connection */
Harald Welte6811d102019-04-14 22:23:14 +020066type component BSC_ConnHdlr extends RAN_ConnHdlr, MNCC_ConnHdlr, GSUP_ConnHdlr, MGCP_ConnHdlr, SMPP_ConnHdlr, CTRL_Adapter_CT, SGsAP_ConnHdlr {
Harald Weltea49e36e2018-01-21 19:29:33 +010067 var BSC_ConnHdlrPars g_pars;
Harald Weltea10db902018-01-27 12:44:49 +010068 timer g_Tguard := 60.0;
Philipp Maierc39a9d82018-11-09 11:21:08 +010069 port TELNETasp_PT MSCVTY;
Harald Weltea49e36e2018-01-21 19:29:33 +010070}
71
Harald Weltede371492018-01-27 23:44:41 +010072type record BSC_ConnHdlrNetworkPars {
73 OCT1 kc_support,
74 boolean expect_tmsi,
75 boolean expect_auth,
Oliver Smith1d118ff2019-07-03 10:57:35 +020076 boolean expect_ciph,
77 boolean expect_imei,
78 boolean expect_imei_early,
79 GSUP_IMEIResult check_imei_result,
80 boolean check_imei_error
Harald Weltede371492018-01-27 23:44:41 +010081}
82
Harald Weltea49e36e2018-01-21 19:29:33 +010083type record BSC_ConnHdlrPars {
84 SCCP_PAR_Address sccp_addr_own,
85 SCCP_PAR_Address sccp_addr_peer,
86 BSSMAP_IE_CellIdentifier cell_id,
Harald Welte256571e2018-01-24 18:47:19 +010087 hexstring imei,
Harald Weltea49e36e2018-01-21 19:29:33 +010088 hexstring imsi,
Harald Welte82600572018-01-21 20:54:08 +010089 hexstring msisdn,
Harald Welte256571e2018-01-24 18:47:19 +010090 OCT4 tmsi optional,
Harald Welte9de84792018-01-28 01:06:35 +010091 MobileStationClassmark1_V cm1,
Harald Welte82600572018-01-21 20:54:08 +010092 BSSMAP_IE_ClassmarkInformationType2 cm2,
Harald Welte16114282018-01-24 22:41:21 +010093 BSSMAP_IE_ClassmarkInformationType3 cm3 optional,
Harald Weltede371492018-01-27 23:44:41 +010094 AuthVector vec optional,
Neels Hofmeyrc1f105a2018-03-01 20:00:19 +010095 BSC_ConnHdlrNetworkPars net,
Philipp Maieraeb29a82018-11-08 17:40:53 +010096 boolean send_early_cm,
97 charstring ipa_ctrl_ip,
98 integer ipa_ctrl_port,
Philipp Maier9b690e42018-12-21 11:50:03 +010099 boolean ipa_ctrl_enable,
Philipp Maier57865482019-01-07 18:33:13 +0100100 boolean mm_info,
Philipp Maierc09a1312019-04-09 16:05:26 +0200101 boolean sgsap_enable,
Harald Weltef9abf8d2019-04-21 13:07:17 +0200102 boolean gsup_enable,
Vadim Yanitskiy2dd96612020-01-07 21:48:29 +0100103 OCT4 gsup_sid,
Harald Weltec1f937a2019-04-21 21:19:23 +0200104 integer ran_idx,
Harald Welte9b751a62019-04-14 17:39:29 +0200105 boolean use_umts_aka,
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200106 boolean ran_is_geran,
Neels Hofmeyr4e18ccc2020-06-24 19:08:17 +0200107 boolean use_osmux,
Pau Espin Pedrol833174e2020-09-03 16:46:02 +0200108 boolean use_ipv6,
Neels Hofmeyr4e18ccc2020-06-24 19:08:17 +0200109 boolean verify_cell_id
Harald Weltea49e36e2018-01-21 19:29:33 +0100110};
111
Harald Welte9de84792018-01-28 01:06:35 +0100112/* get a one-octet bitmaks of supported algorithms based on Classmark information */
113function f_alg_mask_from_cm(BSSMAP_IE_ClassmarkInformationType2 cm2) return OCT1 {
114 var BIT8 res := '00000001'B; /* A5/0 always supported */
115
116 if (cm2.a5_1 == '0'B) {
117 res := res or4b '00000010'B;
118 }
119 if (cm2.classmarkInformationType2_oct5.a5_2 == '1'B ) {
120 res := res or4b '00000100'B;
121 }
122 if (cm2.classmarkInformationType2_oct5.a5_3 == '1'B) {
123 res := res or4b '00001000'B;
124 }
125 /* TODO: CM3 for A5/4 and beyond */
126 return bit2oct(res);
127}
128
129/* determine the best algorithm available within the bit-mask */
130function f_best_alg_from_mask(OCT1 alg_in) return OCT1 {
131 var BIT8 alg := oct2bit(alg_in);
132 var BIT8 ordered_algs[8] := {
133 '10000000'B, '01000000'B, '00100000'B, '00010000'B,
134 '00001000'B, /* A5/3 */
135 '00000010'B, /* A5/1 */
136 '00000100'B, /* A5/2 */
137 '00000001'B /* A5/0 */ }
138 for (var integer i := 0; i < sizeof(ordered_algs); i := i+1) {
139 if (alg and4b ordered_algs[i] != '00000000'B) {
140 return bit2oct(ordered_algs[i]);
141 }
142 }
143 return '00'O;
144}
145
146/* return an integer like '1' for A5/1 based on a mask (with only one bit set */
147function f_alg_from_mask(OCT1 mask_in) return integer {
148 var BIT8 mask := oct2bit(mask_in);
149 for (var integer i := 0; i < 8; i := i+1) {
150 if (mask and4b ('00000001'B << i) != '00000000'B) {
151 return i;
152 }
153 }
154 return -1;
155}
156
Harald Weltea10db902018-01-27 12:44:49 +0100157/* altstep for the global guard timer */
158private altstep as_Tguard() runs on BSC_ConnHdlr {
159 [] g_Tguard.timeout {
Daniel Willmann90829d62018-02-15 17:45:14 +0100160 setverdict(fail, "Tguard timeout");
Daniel Willmannafce8662018-07-06 23:11:32 +0200161 mtc.stop;
Harald Weltea10db902018-01-27 12:44:49 +0100162 }
163}
164
165/* init function, called as first function in new BSC_ConnHdlr */
Harald Weltead2952e2018-01-27 14:12:46 +0100166function f_init_handler(BSC_ConnHdlrPars pars, float t_guard := 60.0) runs on BSC_ConnHdlr {
Harald Weltea10db902018-01-27 12:44:49 +0100167 /* make parameters available via component variable */
168 g_pars := pars;
169 /* Start guard timer and activate it as default */
Harald Weltead2952e2018-01-27 14:12:46 +0100170 g_Tguard.start(t_guard);
Harald Weltea10db902018-01-27 12:44:49 +0100171 activate(as_Tguard());
Harald Weltef640a012018-04-14 17:49:21 +0200172 /* Route all SMPP messages for our MSISDN to us */
173 f_create_smpp_expect(hex2str(pars.msisdn));
Philipp Maier57865482019-01-07 18:33:13 +0100174
Harald Welte4263c522018-12-06 11:56:27 +0100175 /* Route all SGs message for our IMSI to us */
Philipp Maier57865482019-01-07 18:33:13 +0100176 if (g_pars.sgsap_enable == true) {
177 f_create_sgsap_expect(pars.imsi);
178 }
Philipp Maieraeb29a82018-11-08 17:40:53 +0100179
180 if (g_pars.ipa_ctrl_enable == true) {
Pau Espin Pedrol9a5b8ff2021-01-04 19:01:31 +0100181 f_ipa_ctrl_start_client(g_pars.ipa_ctrl_ip, g_pars.ipa_ctrl_port);
Philipp Maieraeb29a82018-11-08 17:40:53 +0100182 }
Philipp Maierc39a9d82018-11-09 11:21:08 +0100183
184 map(self:MSCVTY, system:MSCVTY);
185 f_vty_set_prompts(MSCVTY);
186 f_vty_transceive(MSCVTY, "enable");
Harald Weltea10db902018-01-27 12:44:49 +0100187}
188
Harald Weltea49e36e2018-01-21 19:29:33 +0100189
Harald Welte6811d102019-04-14 22:23:14 +0200190/* Callback function from general RAN_Emulation whenever a connectionless
Harald Weltea49e36e2018-01-21 19:29:33 +0100191 * BSSMAP message arrives. Canreturn a PDU_BSSAPthat should be sent in return */
192private function BscUnitdataCallback(PDU_BSSAP bssap)
Harald Welte6811d102019-04-14 22:23:14 +0200193runs on RAN_Emulation_CT return template PDU_BSSAP {
Harald Weltea49e36e2018-01-21 19:29:33 +0100194 var template PDU_BSSAP resp := omit;
195
196 log("BSSMAP_BscUnitdataCallback");
197 /* answer all RESET with RESET ACK */
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200198 if (match(bssap, tr_BSSMAP_Reset(g_ran_ops.use_osmux))){
Harald Weltea49e36e2018-01-21 19:29:33 +0100199 log("BSSMAP_BscUnitdataCallback: Responding to RESET with RESET-ACK");
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200200 resp := ts_BSSMAP_ResetAck(g_ran_ops.use_osmux);
Harald Weltea49e36e2018-01-21 19:29:33 +0100201 }
202
203 /* FIXME: Handle paging, etc. */
204 return resp;
205}
206
Harald Welte9b751a62019-04-14 17:39:29 +0200207private function RncUnitdataCallback(RANAP_PDU ranap)
208runs on RAN_Emulation_CT return template RANAP_PDU {
209 var template RANAP_PDU resp := omit;
210
211 log("RANAP_RncUnitdataCallback");
212 /* answer all RESET with RESET ACK */
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200213 if (match(ranap, tr_RANAP_Reset())) {
Harald Welte9b751a62019-04-14 17:39:29 +0200214 log("RANAP_RncUnitdataCallback: Responding to RESET with RESET-ACK");
215 var CN_DomainIndicator dom;
216 dom := ranap.initiatingMessage.value_.Reset.protocolIEs[1].value_.cN_DomainIndicator;
217 resp := ts_RANAP_ResetAck(dom);
218 }
219
220 /* FIXME: Handle paging, etc. */
221 return resp;
222}
223
224
Harald Welte6811d102019-04-14 22:23:14 +0200225const RanOps BSC_RanOps := {
Harald Weltea49e36e2018-01-21 19:29:33 +0100226 /* Create call-back for inbound connections from MSC (hand-over) */
Harald Welte6811d102019-04-14 22:23:14 +0200227 create_cb := refers(RAN_Emulation.ExpectedCreateCallback),
Harald Weltea49e36e2018-01-21 19:29:33 +0100228 unitdata_cb := refers(BscUnitdataCallback),
Harald Welte9b751a62019-04-14 17:39:29 +0200229 ranap_create_cb := refers(RAN_Emulation.RanapExpectedCreateCallback),
230 ranap_unitdata_cb := refers(RncUnitdataCallback),
231 ps_domain := false,
Harald Weltea49e36e2018-01-21 19:29:33 +0100232 decode_dtap := true,
Harald Weltea4ca4462018-02-09 00:17:14 +0100233 role_ms := true,
Harald Welte2fce7882019-04-15 11:48:05 +0200234 protocol := RAN_PROTOCOL_BSSAP,
Pau Espin Pedrolc6b78ff2019-06-06 15:58:17 +0200235 transport := BSSAP_TRANSPORT_AoIP,
Pau Espin Pedrolc6a53db2019-05-20 19:31:47 +0200236 use_osmux := false,
Harald Weltea4ca4462018-02-09 00:17:14 +0100237 sccp_addr_local := omit,
238 sccp_addr_peer := omit
Harald Weltea49e36e2018-01-21 19:29:33 +0100239}
240
241
242private function MnccUnitdataCallback(MNCC_PDU mncc)
243runs on MNCC_Emulation_CT return template MNCC_PDU {
244 log("Ignoring MNCC", mncc);
245 return omit;
246}
247
248const MnccOps BCC_MnccOps := {
249 create_cb := refers(MNCC_Emulation.ExpectedCreateCallback),
250 unitdata_cb := refers(MnccUnitdataCallback)
251}
252
253
254
Harald Welte6811d102019-04-14 22:23:14 +0200255/* Encode 'l3' and ask RAN_Emulation to create new connection with COMPL L3 INFO */
Harald Weltea49e36e2018-01-21 19:29:33 +0100256function f_bssap_compl_l3(PDU_ML3_MS_NW l3)
257runs on BSC_ConnHdlr {
258 log("Sending COMPL L3: ", l3);
259 var octetstring l3_enc := enc_PDU_ML3_MS_NW(l3);
260 BSSAP.send(ts_BSSAP_Conn_Req(g_pars.sccp_addr_peer, g_pars.sccp_addr_own,
261 valueof(ts_BSSMAP_ComplL3(g_pars.cell_id, l3_enc))));
Harald Welte71b69332018-01-21 20:43:53 +0100262 alt {
Harald Welte6811d102019-04-14 22:23:14 +0200263 [] BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND) {}
264 [] BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {
Harald Welte71b69332018-01-21 20:43:53 +0100265 setverdict(fail, "DISC.ind from SCCP");
Daniel Willmannafce8662018-07-06 23:11:32 +0200266 mtc.stop;
Harald Welte71b69332018-01-21 20:43:53 +0100267 }
268 }
Harald Weltea49e36e2018-01-21 19:29:33 +0100269}
270
Harald Welte9b751a62019-04-14 17:39:29 +0200271/* generate Iu LAI from BSSAP CGI */
272private function f_IuLAI_from_BssmapCI(BSSMAP_IE_CellIdentifier ci) return LAI {
273 var LAI lai;
274 if (ischosen(ci.cellIdentification.cI_CGI)) {
275 lai.pLMNidentity := ci.cellIdentification.cI_CGI.mcc_mnc;
276 lai.lAC := ci.cellIdentification.cI_CGI.lac;
277 } else if (ischosen(ci.cellIdentification.cI_SAI)) {
278 lai.pLMNidentity := ci.cellIdentification.cI_SAI.mcc_mnc;
279 lai.lAC := ci.cellIdentification.cI_SAI.lac;
280 } else if (ischosen(ci.cellIdentification.ci_LAC_RNC_CI)) {
281 lai.pLMNidentity := ci.cellIdentification.ci_LAC_RNC_CI.mcc_mnc;
282 lai.lAC := ci.cellIdentification.ci_LAC_RNC_CI.lac;
283 } else {
284 mtc.stop;
285 }
286 lai.iE_Extensions := omit;
287 return lai;
288}
289
290/* like f_bssap_compl_l3() but for 3G */
291function f_ranap_initial_ue(PDU_ML3_MS_NW l3)
292runs on BSC_ConnHdlr {
293 log("Sending InitialUE: ", l3);
294 var octetstring l3_enc := enc_PDU_ML3_MS_NW(l3);
295 var RANAP_PDU ranap;
296 var LAI lai := f_IuLAI_from_BssmapCI(g_pars.cell_id);
297 var SAI sai := {
298 pLMNidentity := lai.pLMNidentity,
299 lAC := lai.lAC,
300 sAC := '0000'O, /* FIXME */
301 iE_Extensions := omit
302 };
303 var IuSignallingConnectionIdentifier sigc_id := int2bit(23, 24);
304 var GlobalRNC_ID grnc_id := {
305 pLMNidentity := lai.pLMNidentity,
306 rNC_ID := 2342 /* FIXME */
307 };
308
309 ranap := valueof(ts_RANAP_initialUE_CS(lai, sai, l3_enc, sigc_id, grnc_id));
310 BSSAP.send(ts_RANAP_Conn_Req(g_pars.sccp_addr_peer, g_pars.sccp_addr_own, ranap));
311 alt {
312 [] BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_CONF_IND) {}
313 [] BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {
314 setverdict(fail, "DISC.ind from SCCP");
315 mtc.stop;
316 }
317 }
318}
319
Harald Weltedceacc72019-04-21 20:58:35 +0200320/* Send BSSMAP Complete L3 or RANAP Initial UE depending on 2G/3G RAN type */
321function f_cl3_or_initial_ue(PDU_ML3_MS_NW l3)
322runs on BSC_ConnHdlr {
323 if (g_pars.ran_is_geran) {
324 f_bssap_compl_l3(l3);
325 } else {
326 f_ranap_initial_ue(l3);
327 }
328}
329
Harald Welte081b19a2018-02-10 09:11:13 +0100330type enumerated EstablishType {
331 EST_TYPE_MO_CALL,
Harald Welte0bef21e2018-02-10 09:48:23 +0100332 EST_TYPE_EMERG_CALL,
Harald Weltef45efeb2018-04-09 18:19:24 +0200333 EST_TYPE_PAG_RESP,
Vadim Yanitskiy20ee5e42018-05-27 17:54:21 +0700334 EST_TYPE_MO_SMS,
335 EST_TYPE_SS_ACT
Harald Welte081b19a2018-02-10 09:11:13 +0100336};
337
Harald Weltea49e36e2018-01-21 19:29:33 +0100338/* helper function to fully establish a dedicated channel */
Harald Welteb9e86fa2018-04-09 18:18:31 +0200339function f_establish_fully(EstablishType etype := EST_TYPE_MO_CALL)
Harald Weltea49e36e2018-01-21 19:29:33 +0100340runs on BSC_ConnHdlr {
Harald Welte081b19a2018-02-10 09:11:13 +0100341 var PDU_ML3_MS_NW l3_info;
Harald Welteb9e86fa2018-04-09 18:18:31 +0200342 var MobileIdentityLV mi;
343
344 /* If we have a TMSI, use TMSI instead of IMSI */
345 if (ispresent(g_pars.tmsi)) {
346 mi := valueof(ts_MI_TMSI_LV(g_pars.tmsi));
347 } else {
348 mi := valueof(ts_MI_IMSI_LV(g_pars.imsi));
349 }
350
Harald Welte081b19a2018-02-10 09:11:13 +0100351 select (etype) {
352 case (EST_TYPE_MO_CALL) {
353 l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_CALL, mi));
354 }
Harald Welte0bef21e2018-02-10 09:48:23 +0100355 case (EST_TYPE_EMERG_CALL) {
356 l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_EMERG_CALL, mi));
357 }
Harald Welte081b19a2018-02-10 09:11:13 +0100358 case (EST_TYPE_PAG_RESP) {
359 l3_info := valueof(ts_PAG_RESP(mi));
360 }
Harald Weltef45efeb2018-04-09 18:19:24 +0200361 case (EST_TYPE_MO_SMS) {
362 l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_MO_SMS, mi));
363 }
Vadim Yanitskiy20ee5e42018-05-27 17:54:21 +0700364 case (EST_TYPE_SS_ACT) {
365 l3_info := valueof(ts_CM_SERV_REQ(CM_TYPE_SS_ACT, mi));
366 }
Harald Welte081b19a2018-02-10 09:11:13 +0100367 }
Harald Weltea49e36e2018-01-21 19:29:33 +0100368
369 /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */
Harald Weltedceacc72019-04-21 20:58:35 +0200370 f_cl3_or_initial_ue(l3_info);
Harald Weltea49e36e2018-01-21 19:29:33 +0100371
Neels Hofmeyr4e18ccc2020-06-24 19:08:17 +0200372 f_verify_vty_lac_ci(verify_vlr := false);
373
Harald Weltede371492018-01-27 23:44:41 +0100374 f_mm_common();
Pau Espin Pedrolf8034482019-06-03 13:15:58 +0200375 if (g_pars.net.expect_ciph or not g_pars.ran_is_geran) {
Harald Welte148a7082018-01-26 18:56:43 +0100376 /* implicit CM SERVICE ACCEPT? */
377 } else {
Harald Welte0bef21e2018-02-10 09:48:23 +0100378 if (etype != EST_TYPE_PAG_RESP) {
Harald Welte081b19a2018-02-10 09:11:13 +0100379 /* explicit CM SERVICE ACCEPT */
380 BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_ACC));
381 }
Harald Weltea49e36e2018-01-21 19:29:33 +0100382 }
Harald Weltea49e36e2018-01-21 19:29:33 +0100383}
384
385/* build a PDU_ML3_MS_NW containing a Location Update by IMSI */
Harald Welte9de84792018-01-28 01:06:35 +0100386function f_build_lu_imsi(hexstring imsi) runs on BSC_ConnHdlr return PDU_ML3_MS_NW
Harald Weltea49e36e2018-01-21 19:29:33 +0100387{
388 var MobileIdentityLV mi := valueof(ts_MI_IMSI_LV(imsi));
389 return f_build_lu(mi);
390}
Harald Welte9de84792018-01-28 01:06:35 +0100391function f_build_lu_imei(hexstring imei) runs on BSC_ConnHdlr return PDU_ML3_MS_NW
Harald Welteba7b6d92018-01-23 21:32:34 +0100392{
393 var MobileIdentityLV mi := valueof(ts_MI_IMEI_LV(imei));
394 return f_build_lu(mi);
395}
Harald Welte9de84792018-01-28 01:06:35 +0100396function f_build_lu_tmsi(OCT4 tmsi) runs on BSC_ConnHdlr return PDU_ML3_MS_NW
Harald Welteba7b6d92018-01-23 21:32:34 +0100397{
398 var MobileIdentityLV mi := valueof(ts_MI_TMSI_LV(tmsi));
399 return f_build_lu(mi);
400}
Harald Welte9de84792018-01-28 01:06:35 +0100401private function f_build_lu(MobileIdentityLV mi) runs on BSC_ConnHdlr return PDU_ML3_MS_NW
Harald Weltea49e36e2018-01-21 19:29:33 +0100402{
403 var LocationAreaIdentification_V old_lai := { '62F220'O, '9999'O };
404 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 +0100405 old_lai, mi, g_pars.cm1));
Harald Weltea49e36e2018-01-21 19:29:33 +0100406 return l3_info;
407}
408
Harald Weltea183a5d2019-05-09 13:40:52 +0200409altstep as_GSUP_SAI() runs on BSC_ConnHdlr {
410var GSUP_IE auth_tuple;
411[] GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)) {
412 if (g_pars.use_umts_aka) {
413 g_pars.vec := f_gen_auth_vec_3g();
414 auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G3G(g_pars.vec.rand,
415 g_pars.vec.sres,
416 g_pars.vec.kc,
417 g_pars.vec.ik,
418 g_pars.vec.ck,
419 g_pars.vec.autn,
420 g_pars.vec.res));
421 GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple));
422 } else {
423 g_pars.vec := f_gen_auth_vec_2g();
424 auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G(g_pars.vec.rand,
425 g_pars.vec.sres,
426 g_pars.vec.kc));
427 GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple));
428 }
429 }
430}
431
Harald Welte9de84792018-01-28 01:06:35 +0100432function f_mm_auth() runs on BSC_ConnHdlr
Harald Welte148a7082018-01-26 18:56:43 +0100433{
Harald Weltede371492018-01-27 23:44:41 +0100434 if (g_pars.net.expect_auth) {
Harald Weltea183a5d2019-05-09 13:40:52 +0200435 as_GSUP_SAI();
Harald Weltec1f937a2019-04-21 21:19:23 +0200436 if (g_pars.use_umts_aka) {
Harald Weltec1f937a2019-04-21 21:19:23 +0200437 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_AUTH_REQ_3G(g_pars.vec.rand, g_pars.vec.autn)));
438 var OCT4 res := substr(g_pars.vec.res, 0, 4);
439 var OCT4 xres := substr(g_pars.vec.res, 4, 4);
440 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MT_MM_AUTH_RESP_3G(res, xres)));
441 } else {
Harald Weltec1f937a2019-04-21 21:19:23 +0200442 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_AUTH_REQ(g_pars.vec.rand)));
443 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MT_MM_AUTH_RESP_2G(g_pars.vec.sres)));
444 }
Harald Welte148a7082018-01-26 18:56:43 +0100445 }
Harald Welte9de84792018-01-28 01:06:35 +0100446}
Harald Welte148a7082018-01-26 18:56:43 +0100447
Oliver Smith1d118ff2019-07-03 10:57:35 +0200448function f_mm_imei() runs on BSC_ConnHdlr
449{
450 var PDU_DTAP_MT dtap_mt;
451 var GSUP_PDU gsup_msg;
Vadim Yanitskiy98bb2d52020-03-28 00:57:21 +0700452 var MobileIdentityLV mi;
Oliver Smith1d118ff2019-07-03 10:57:35 +0200453
454 if (not g_pars.net.expect_imei) {
455 return
456 }
457
458 /* MSC <-> BSC: ID req/rsp for IMEI */
459 alt {
460 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_ID_Req(CM_ID_TYPE_IMEI))) {
461 mi := valueof(ts_MI_IMEI_LV(g_pars.imei));
462 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_MM_ID_Rsp(mi)));
463 }
464 [] BSSAP.receive(tr_PDU_DTAP_MT(?)) -> value dtap_mt {
465 setverdict(fail, "Expected ID REQ for IMEI DTAP MT message, but got: ", dtap_mt);
466 mtc.stop;
467 }
468 }
469
470 /* MSC <-> HLR: Check IMEI req/res/err */
471 alt {
472 [g_pars.net.check_imei_error] GSUP.receive(tr_GSUP_CHECK_IMEI_REQ(g_pars.imsi, g_pars.imei)) {
473 GSUP.send(ts_GSUP_CHECK_IMEI_ERR(g_pars.imsi, 96 /* Invalid Mandatory Information */));
474 }
475 [not g_pars.net.check_imei_error] GSUP.receive(tr_GSUP_CHECK_IMEI_REQ(g_pars.imsi, g_pars.imei)) {
476 GSUP.send(ts_GSUP_CHECK_IMEI_RES(g_pars.imsi, g_pars.net.check_imei_result));
477 }
478 [] GSUP.receive(?) -> value gsup_msg {
479 setverdict(fail, "Expected CHECK IMEI REQ GSUP message (with IMEI:", g_pars.imei, " and IMSI: ",
480 g_pars.imsi, "), but got: ", gsup_msg);
481 mtc.stop;
482 }
483 }
484}
485
486function f_mm_imei_early() runs on BSC_ConnHdlr
487{
488 var PDU_DTAP_MT dtap_mt;
489 var GSUP_PDU gsup_msg;
Vadim Yanitskiy98bb2d52020-03-28 00:57:21 +0700490 var MobileIdentityLV mi;
Oliver Smith1d118ff2019-07-03 10:57:35 +0200491
492 if (not g_pars.net.expect_imei_early) {
493 return
494 }
495
496 /* MSC <-> BSC: ID req/rsp for IMEISV */
497 alt {
498 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_ID_Req(CM_ID_TYPE_IMEISV))) {
499 mi := valueof(ts_MI_IMEISV_LV(g_pars.imei));
500 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_MM_ID_Rsp(mi)));
501 }
502 [] BSSAP.receive(tr_PDU_DTAP_MT(?)) -> value dtap_mt {
503 setverdict(fail, "Expected ID REQ for IMEISV DTAP MT message, but got: ", dtap_mt);
504 mtc.stop;
505 }
506 }
507
508 /* MSC <-> HLR: Check IMEI req/res/err */
509 alt {
510 [g_pars.net.check_imei_error] GSUP.receive(tr_GSUP_CHECK_IMEI_REQ(g_pars.imsi, g_pars.imei)) {
511 GSUP.send(ts_GSUP_CHECK_IMEI_ERR(g_pars.imsi, 96 /* Invalid Mandatory Information */));
512 }
513 [not g_pars.net.check_imei_error] GSUP.receive(tr_GSUP_CHECK_IMEI_REQ(g_pars.imsi, g_pars.imei)) {
514 GSUP.send(ts_GSUP_CHECK_IMEI_RES(g_pars.imsi, g_pars.net.check_imei_result));
515 }
516 [] GSUP.receive(?) -> value gsup_msg {
517 setverdict(fail, "Expected CHECK IMEI REQ GSUP message (with IMEI:", g_pars.imei, " and IMSI: ",
518 g_pars.imsi, "), but got: ", gsup_msg);
519 mtc.stop;
520 }
521 }
522}
523
Harald Welte79f1e452020-08-18 22:55:02 +0200524function f_expect_common_id() runs on BSC_ConnHdlr
525{
526 if (g_pars.ran_is_geran) {
Pau Espin Pedrol174fac22021-02-26 13:20:10 +0100527 BSSAP.receive(tr_BSSMAP_CommonId(g_pars.imsi));
Harald Welte79f1e452020-08-18 22:55:02 +0200528 } else {
529 BSSAP.receive(tr_RANAP_CommonId(imsi_hex2oct(g_pars.imsi)));
530 }
531}
532
Harald Welte9de84792018-01-28 01:06:35 +0100533function f_mm_common() runs on BSC_ConnHdlr
534{
535 f_mm_auth();
Harald Welte9b751a62019-04-14 17:39:29 +0200536 if (g_pars.ran_is_geran) {
537 if (g_pars.net.expect_ciph) {
538 var OCT1 a5_net := f_alg_mask_from_cm(g_pars.cm2);
539 var OCT1 a5_intersect := g_pars.net.kc_support and4b a5_net;
540 alt {
541 [] BSSAP.receive(tr_BSSMAP_CipherModeCmd(a5_intersect, g_pars.vec.kc)) {
542 var OCT1 a5_chosen := f_best_alg_from_mask(a5_intersect);
543 var integer a5_nr := f_alg_from_mask(a5_chosen);
544 BSSAP.send(ts_BSSMAP_CipherModeCompl(int2oct(a5_nr+1, 1)));
545 }
546 [] BSSAP.receive(tr_BSSMAP_CipherModeCmd(?, g_pars.vec.kc)) {
547 setverdict(fail, "Wrong ciphering algorithm mask in CiphModCmd");
548 mtc.stop;
549 }
Harald Welte9de84792018-01-28 01:06:35 +0100550 }
Harald Welte9b751a62019-04-14 17:39:29 +0200551 /* FIXME: Send the best available algorithm */
552 }
Harald Welte79f1e452020-08-18 22:55:02 +0200553 f_expect_common_id();
Harald Welte9b751a62019-04-14 17:39:29 +0200554 } else { /* UTRAN */
555 alt {
556 [g_pars.net.expect_ciph] BSSAP.receive(tr_RANAP_SecurityModeCmdEnc(uia_algs := ?,
557 uia_key := oct2bit(g_pars.vec.ik),
558 key_sts := ?,
559 uea_algs := ?,
560 uea_key := oct2bit(g_pars.vec.ck))) {
561 var IntegrityProtectionAlgorithm uia_chosen := 0; /*standard_UMTS_integrity_algorithm_UIA1*/
562 var EncryptionAlgorithm uea_chosen := 1; /*standard_UMTS_encryption_algorith_UEA1*/
563 BSSAP.send(ts_RANAP_SecurityModeCompleteEnc(uia_chosen, uea_chosen));
Harald Welte79f1e452020-08-18 22:55:02 +0200564 f_expect_common_id();
Harald Welte9b751a62019-04-14 17:39:29 +0200565 }
566 [g_pars.net.expect_ciph] BSSAP.receive(tr_RANAP_SecurityModeCmdEnc(?,?,?,?,?)) {
567 setverdict(fail, "Invalid SecurityModeCommand (ciphering case)");
568 mtc.stop;
569 }
570 [not g_pars.net.expect_ciph] BSSAP.receive(tr_RANAP_SecurityModeCmd(uia_algs := ?,
571 uia_key := oct2bit(g_pars.vec.ik),
572 key_sts := ?)) {
573 var IntegrityProtectionAlgorithm uia_chosen := 0; /*standard_UMTS_integrity_algorithm_UIA1;*/
574 BSSAP.send(ts_RANAP_SecurityModeComplete(uia_chosen));
Harald Welte79f1e452020-08-18 22:55:02 +0200575 f_expect_common_id();
Harald Welte9b751a62019-04-14 17:39:29 +0200576 }
577 [not g_pars.net.expect_ciph] BSSAP.receive(tr_RANAP_SecurityModeCmd(?,?,?)) {
578 setverdict(fail, "Invalid SecurityModeCommand (non-ciphering case)");
Daniel Willmannafce8662018-07-06 23:11:32 +0200579 mtc.stop;
Harald Welte9de84792018-01-28 01:06:35 +0100580 }
581 }
Harald Welte148a7082018-01-26 18:56:43 +0100582 }
583}
584
Philipp Maier9b690e42018-12-21 11:50:03 +0100585function f_expect_mm_info() runs on BSC_ConnHdlr {
586 if (g_pars.mm_info == true) {
587 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_Info));
588 }
589}
590
Neels Hofmeyr4e18ccc2020-06-24 19:08:17 +0200591private function f_lac_ci_vty_str(BSSMAP_IE_CellIdentifier cell_id) return charstring
592{
593 return "LAC / cell ID: "
594 & int2str(oct2int(cell_id.cellIdentification.cI_CGI.lac)) & " / "
595 & int2str(oct2int(cell_id.cellIdentification.cI_CGI.ci));
596}
597
598/* Get an IMSI's info and verify that the g_pars.cell_id is reflected in the info.
599 * Verify both the "LAC / cell ID" in the VLR and the "LAC / cell ID" in the "Connection:" part, if any.
600 * If verify_vlr == false, then only verify the "Connection:" part, and fail if there is no "Connection:"; this is
601 * useful when a conn has been established, but the subscriber has not been authenticated, so the VLR does not yet
602 * reflect the new conn's cell ID.
603 */
604function f_verify_vty_lac_ci(boolean verify_vlr := true) runs on BSC_ConnHdlr {
605 if (not g_pars.ran_is_geran) {
606 log("Skipping f_verify_vty_lac_ci(), disabled for Iu");
607 setverdict(pass);
608 return;
609 }
610 if (not g_pars.verify_cell_id) {
611 /* Skip this verification; either the TC expects no cell id to end up being accepted, or this was
612 * disabled globally to test an older osmo-msc which doesn't store the cell id properly yet. */
613 log("Skipping f_verify_vty_lac_ci()");
614 setverdict(pass);
615 return;
616 }
617
618 var charstring vty_cmd := "show subscriber imsi " & hex2str(g_pars.imsi) & " conn";
619 var charstring result := f_vty_transceive_ret(MSCVTY, vty_cmd);
620 var charstring expect_lac_ci := "LAC / cell ID: "
621 & int2str(oct2int(g_pars.cell_id.cellIdentification.cI_CGI.lac)) & " / "
622 & int2str(oct2int(g_pars.cell_id.cellIdentification.cI_CGI.ci));
623
624 var boolean vlr_matches := false;
625 var boolean connection_present := false;
626 var boolean connection_matches := false;
627
628 /* There are two occurences of LAC / cell ID: once for the VLR record, and once for the active connection. The
629 * active connection part starts with 'Connection:'. If there is no active connection, that part is omitted.
630 * So, first find out whether there is a 'Connection:' part. Then verify the LAC / cell ID above 'Connection:'
631 * and below 'Connection:', separately.
632 */
633 var integer connection_start := f_strstr(result, "Connection:");
634 connection_present := (connection_start >= 0);
635
636 var integer lac_ci_match := f_strstr(result, expect_lac_ci);
637 if (connection_present) {
638 if (lac_ci_match > connection_start) {
639 /* The first match is below 'Connection:', so the VLR part above it did not match. */
640 vlr_matches := false;
641 connection_matches := true;
642 } else if (lac_ci_match >= 0) {
643 /* The first match is above 'Connection:', so the VLR part matches. */
644 vlr_matches := true;
645
646 /* Now find a match below 'Connection:' */
647 lac_ci_match := f_strstr(result, expect_lac_ci, connection_start);
648 connection_matches := (lac_ci_match > 0);
649 }
650 } else {
651 /* There is no 'Connection:', so a match, if any, is from the VLR part. */
652 vlr_matches := (lac_ci_match >= 0);
653 }
654
655 if (verify_vlr) {
656 if (not vlr_matches) {
657 setverdict(fail, vty_cmd, " shows mismatching LAC / cell ID in the VLR part, expecting: ",
658 expect_lac_ci, " -- got: ", result);
659 return;
660 } else {
661 log("f_verify_vty_lac_ci(): VLR record matches ", expect_lac_ci);
662 setverdict(pass);
663 }
664 }
665
666 if (connection_present) {
667 if (not connection_matches) {
668 setverdict(fail, vty_cmd, " shows mismatching LAC cell ID in the 'Connection' part, expecting: ",
669 expect_lac_ci, " -- got: ", result);
670 } else {
671 log("f_verify_vty_lac_ci(): Active connection matches ", expect_lac_ci);
672 setverdict(pass);
673 }
674 }
675
676 if (not verify_vlr and not connection_present) {
677 setverdict(fail, "f_verify_vty_lac_ci(verify_vlr := false) called, which requires an active connection, but there is no 'Connection:' part to verify in ", result);
678 }
679}
680
Neels Hofmeyrc1f105a2018-03-01 20:00:19 +0100681function f_perform_lu()
Harald Weltea49e36e2018-01-21 19:29:33 +0100682runs on BSC_ConnHdlr {
683 var PDU_ML3_MS_NW l3_lu := f_build_lu_imsi(g_pars.imsi)
684 var PDU_DTAP_MT dtap_mt;
685
686 /* tell GSUP dispatcher to send this IMSI to us */
687 f_create_gsup_expect(hex2str(g_pars.imsi));
688
689 /* Send BSSAP_Conn_Req with COMPL L3 INFO to MSC */
Harald Welte9b751a62019-04-14 17:39:29 +0200690 if (g_pars.ran_is_geran) {
691 f_bssap_compl_l3(l3_lu);
692 if (g_pars.send_early_cm) {
693 BSSAP.send(ts_BSSMAP_ClassmarkUpd(g_pars.cm2, g_pars.cm3));
694 }
695 } else {
696 f_ranap_initial_ue(l3_lu);
Harald Welte8a121b32018-01-22 03:00:41 +0100697 }
Harald Welte5c2622c2018-01-21 20:45:20 +0100698
Neels Hofmeyr4e18ccc2020-06-24 19:08:17 +0200699 /* at this point the conn has been established, but the subscriber has not been authenticated, so the VLR does
700 * not yet reflect this conn's cell ID. */
701 f_verify_vty_lac_ci(verify_vlr := false);
702
Oliver Smith1d118ff2019-07-03 10:57:35 +0200703 f_mm_imei_early();
Harald Weltede371492018-01-27 23:44:41 +0100704 f_mm_common();
Stefan Sperlinga2d59c62018-12-18 16:32:44 +0100705 f_msc_lu_hlr();
Oliver Smith1d118ff2019-07-03 10:57:35 +0200706 f_mm_imei();
Stefan Sperlinga2d59c62018-12-18 16:32:44 +0100707 f_accept_reject_lu();
708 /* FIXME: there could be pending SMS or other common procedures by the MSC, let's ignore them */
709 f_expect_clear();
Harald Welte16114282018-01-24 22:41:21 +0100710
Stefan Sperlinga2d59c62018-12-18 16:32:44 +0100711 setverdict(pass);
712}
713
714function f_msc_lu_hlr() runs on BSC_ConnHdlr
715{
Harald Weltea49e36e2018-01-21 19:29:33 +0100716 /* Expect MSC to perform LU with HLR */
717 GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi));
718 GSUP.send(ts_GSUP_ISD_REQ(g_pars.imsi, g_pars.msisdn));
719 GSUP.receive(tr_GSUP_ISD_RES(g_pars.imsi));
720 GSUP.send(ts_GSUP_UL_RES(g_pars.imsi));
Stefan Sperlinga2d59c62018-12-18 16:32:44 +0100721}
722
723function f_accept_reject_lu() runs on BSC_ConnHdlr {
724 var PDU_DTAP_MT dtap_mt;
Harald Weltea49e36e2018-01-21 19:29:33 +0100725
726 alt {
727 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Acc)) -> value dtap_mt {
728 var PDU_ML3_LocationUpdateAccept lu_acc := dtap_mt.dtap.msgs.mm.locationUpdateAccept;
Harald Weltede371492018-01-27 23:44:41 +0100729 if (g_pars.net.expect_tmsi) {
Harald Weltea49e36e2018-01-21 19:29:33 +0100730 if (not ispresent(lu_acc.mobileIdentityTLV) or
731 not ischosen(lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi)) {
732 setverdict(fail, "Expected TMSI but no TMSI was allocated");
Daniel Willmannafce8662018-07-06 23:11:32 +0200733 mtc.stop;
Harald Weltea49e36e2018-01-21 19:29:33 +0100734 } else {
Harald Welte256571e2018-01-24 18:47:19 +0100735 g_pars.tmsi := lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi.octets;
Harald Weltea49e36e2018-01-21 19:29:33 +0100736 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_TmsiRealloc_Cmpl));
737 }
738 } else {
739 if (ispresent(lu_acc.mobileIdentityTLV) and
740 ischosen(lu_acc.mobileIdentityTLV.mobileIdentityLV.mobileIdentityV.oddEvenInd_identity.tmsi_ptmsi)) {
741 setverdict(fail, "Expected no TMSI but TMSI was allocated");
Daniel Willmannafce8662018-07-06 23:11:32 +0200742 mtc.stop;
Harald Weltea49e36e2018-01-21 19:29:33 +0100743 }
744 }
745 }
746 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)) {
747 setverdict(fail, "Expected LU ACK, but received LU REJ");
Daniel Willmannafce8662018-07-06 23:11:32 +0200748 mtc.stop;
Harald Weltea49e36e2018-01-21 19:29:33 +0100749 }
750 }
Philipp Maier9b690e42018-12-21 11:50:03 +0100751
752 /* Wait for MM-Information (if enabled) */
753 f_expect_mm_info();
Harald Weltea49e36e2018-01-21 19:29:33 +0100754 setverdict(pass);
755}
756
Pau Espin Pedrold3d54a92019-12-17 17:02:54 +0100757function f_expect_lu_reject(template OCT1 cause := ?) runs on BSC_ConnHdlr {
Oliver Smith91bfa1c2019-07-19 15:01:15 +0200758 var PDU_DTAP_MT dtap_mt;
759 timer T := 5.0;
760
761 T.start;
762 alt {
Pau Espin Pedrold3d54a92019-12-17 17:02:54 +0100763 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej(cause))) {
Oliver Smith91bfa1c2019-07-19 15:01:15 +0200764 setverdict(pass);
765 }
766 [] BSSAP.receive(tr_PDU_DTAP_MT(?)) -> value dtap_mt {
767 setverdict(fail, "Expected LU reject BSSAP message, got: ", dtap_mt);
768 }
769 [] T.timeout {
770 setverdict(fail, "Timeout waiting for LU reject");
771 }
772 }
773}
774
Harald Weltea49e36e2018-01-21 19:29:33 +0100775function f_foo() runs on BSC_ConnHdlr{
Harald Welte6811d102019-04-14 22:23:14 +0200776 /* SCCP CC handled by RAN_Emulation_CT.main() */
Harald Weltea49e36e2018-01-21 19:29:33 +0100777 /* Expect auth, if enabled */
778
779 /* TODO: ISD */
780 /* Expect encr, if enabled */
781 /* Expect encr, if enabled */
782 /* Expect ASS CMD, if chan_type != requested */
783 /* Send ASS CMPL in successful case */
784
785 /* Expect AoIP port/ip information for RTP stream */
786 /* Expect MSC-originated MGCP to our simulated MGW */
787 /* Verify Counters via CTRL */
788 /* re-configure MSC behaviour via VTY */
789}
790
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200791type record CrcxResponse {
792 integer resp, /* 1 = reply with OK, 0 = do not reply, -1 = reply with error */
793 HostName mgw_rtp_ip,
794 PortNumber mgw_rtp_port,
795 MgcpConnectionId mgcp_connection_id /* MGCP Connection ID BSS Side */
796}
797
Harald Welteb71901a2018-01-26 19:16:05 +0100798/* parameters related to a (MO?) voice call */
799type record CallParameters {
Harald Welteb71901a2018-01-26 19:16:05 +0100800 /* CC related parameters */
801 hexstring called_party, /* whom are we calling */
802 integer transaction_id optional, /* which TS 04.08 CC transaction ID to use */
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100803 boolean mo_call, /* For a MO call, the transaction_id was allocated by the MS,
804 important to set the TI flag properly */
Harald Welteb71901a2018-01-26 19:16:05 +0100805 BearerCapability_TLV bearer_cap, /* which bearer capabilities to claim */
Harald Welte0bef21e2018-02-10 09:48:23 +0100806 boolean emergency, /* is this an emergency call? */
Harald Welteb71901a2018-01-26 19:16:05 +0100807
808 /* MNCC related parameters */
809 uint32_t mncc_callref optional, /* call reference on the MNCC side */
810 MNCC_bearer_cap mncc_bearer_cap optional, /* MNCC-side bearer capabilities */
Pau Espin Pedrol563b3d02020-09-09 20:19:52 +0200811 HostName mncc_rtp_ip optional, /* MNCC Side RTP IP */
812 PortNumber mncc_rtp_port optional, /* MNCC Side RTP port */
Harald Welteb71901a2018-01-26 19:16:05 +0100813
814 /* RTP related parameters */
815 HostName bss_rtp_ip optional, /* BSS Side RTP IP */
816 PortNumber bss_rtp_port optional, /* BSS Side RTP Port */
817 HostName mss_rtp_ip optional, /* MSS Side RTP IP */
818 PortNumber mss_rtp_port optional, /* MSS Side RTP Port */
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200819 integer got_crcx_count,
820 CrcxResponse mgw_conn_1,
821 CrcxResponse mgw_conn_2,
Harald Welteb71901a2018-01-26 19:16:05 +0100822 uint7_t rtp_payload_type, /* dynamic RTP payload type */
823 charstring rtp_sdp_format, /* AMR/8000 or the like */
Philipp Maier2a98a732018-03-19 16:06:12 +0100824 boolean mgw_drop_dlcx optional, /* Provoke errors by not responding to DLCX
825 (f_mt_call and f_mt_call) */
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200826 boolean stop_after_cc_setup, /* Special case: stop call establish after CC Setup */
827 boolean ran_clear_when_alerting, /* Special case: send Clear upon CC Alerting */
Neels Hofmeyr8df69622019-11-02 19:16:03 +0100828 boolean expect_release, /* Special case: expect call establish to cause direct CC Rel */
Harald Welteb71901a2018-01-26 19:16:05 +0100829
Philipp Maiercd668572018-03-19 16:11:52 +0100830 MgcpCallId mgcp_call_id optional, /* MGCP Call ID; CallAgent allocated */
Harald Welteb71901a2018-01-26 19:16:05 +0100831 MgcpEndpoint mgcp_ep optional /* MGCP Endpoint, CallAgent or MGW allocated */,
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200832
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200833 boolean use_osmux, /* MSC is expected to use Osmux for this call */
834 integer got_osmux_count
Harald Welteb71901a2018-01-26 19:16:05 +0100835}
836
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200837template (value) CallParameters t_CallParams(hexstring called := '12345'H, integer tid := 0) := {
Harald Welteb71901a2018-01-26 19:16:05 +0100838 called_party := called,
839 transaction_id := tid,
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100840 mo_call := false,
Harald Welteb71901a2018-01-26 19:16:05 +0100841 bearer_cap := valueof(ts_Bcap_voice),
Harald Welte0bef21e2018-02-10 09:48:23 +0100842 emergency := false,
Harald Welteb71901a2018-01-26 19:16:05 +0100843 mncc_callref := omit,
844 mncc_bearer_cap := valueof(ts_MNCC_bcap_voice),
Pau Espin Pedrol563b3d02020-09-09 20:19:52 +0200845 mncc_rtp_ip := "42.23.11.5",
846 mncc_rtp_port := 423,
Harald Welte4017d552018-01-26 21:40:05 +0100847 bss_rtp_ip := "9.8.7.6",
848 bss_rtp_port := 9000,
Harald Welteb71901a2018-01-26 19:16:05 +0100849 mss_rtp_ip := omit,
850 mss_rtp_port := omit,
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200851 got_crcx_count := 0,
852 mgw_conn_1 := {
853 resp := 1,
854 mgw_rtp_ip := "1.1.1.1",
855 mgw_rtp_port := 10000,
856 mgcp_connection_id := '11111'H
857 },
858 mgw_conn_2 := {
859 resp := 1,
Pau Espin Pedrolcb4c59d2020-09-03 20:55:57 +0200860 mgw_rtp_ip := "2.2.2.2",
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200861 mgw_rtp_port := 11000,
862 mgcp_connection_id := '22222'H
863 },
Harald Welteb71901a2018-01-26 19:16:05 +0100864 rtp_payload_type := 98,
865 rtp_sdp_format := "AMR/8000",
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100866 mgw_drop_dlcx := false,
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200867 stop_after_cc_setup := false,
868 ran_clear_when_alerting := false,
Neels Hofmeyr8df69622019-11-02 19:16:03 +0100869 expect_release := false,
Harald Welteb71901a2018-01-26 19:16:05 +0100870 mgcp_call_id := omit,
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200871 mgcp_ep := "rtpbridge/1@mgw",
872 use_osmux := false,
873 got_osmux_count := 0
Harald Welteb71901a2018-01-26 19:16:05 +0100874};
875
Harald Welte4263c522018-12-06 11:56:27 +0100876/* Allocate a call reference and send SETUP via MNCC to MSC */
877function f_mt_call_initate(inout CallParameters cpars)
Harald Welte33ec09b2018-02-10 15:34:46 +0100878runs on BSC_ConnHdlr {
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200879 cpars.mo_call := false;
Harald Welte4263c522018-12-06 11:56:27 +0100880 cpars.mncc_callref := f_rnd_int(2147483648);
881 MNCC.send(ts_MNCC_SETUP_req(cpars.mncc_callref, hex2str(g_pars.msisdn),
882 hex2str(cpars.called_party), hex2str(g_pars.imsi)));
883}
Harald Welte33ec09b2018-02-10 15:34:46 +0100884
Harald Welte408a7ef2019-04-21 17:08:40 +0200885private template (value) SDP_Message ts_SDP_CRCX_CN(CallParameters cpars) :=
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200886 ts_SDP(cpars.mgw_conn_2.mgw_rtp_ip, cpars.mgw_conn_2.mgw_rtp_ip,
Harald Welte408a7ef2019-04-21 17:08:40 +0200887 hex2str(cpars.mgcp_call_id), "42",
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200888 cpars.mgw_conn_2.mgw_rtp_port,
Harald Welte408a7ef2019-04-21 17:08:40 +0200889 { int2str(cpars.rtp_payload_type) },
890 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
891 cpars.rtp_sdp_format)),
892 valueof(ts_SDP_ptime(20)) });
893
Harald Welte4263c522018-12-06 11:56:27 +0100894/* Complete call, begin with a paging response message via BSSAP */
895function f_mt_call_complete(inout CallParameters cpars)
896runs on BSC_ConnHdlr {
Harald Welte33ec09b2018-02-10 15:34:46 +0100897 var MNCC_PDU mncc;
898 var MgcpCommand mgcp_cmd;
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200899 var template MgcpResponse mgcp_resp;
900 var MgcpOsmuxCID osmux_cid;
901 var PDU_BSSAP bssap;
Harald Welte33ec09b2018-02-10 15:34:46 +0100902
Harald Welte6811d102019-04-14 22:23:14 +0200903 f_ran_register_imsi(g_pars.imsi, g_pars.tmsi);
Harald Welte33ec09b2018-02-10 15:34:46 +0100904
Harald Welteb9e86fa2018-04-09 18:18:31 +0200905 f_establish_fully(EST_TYPE_PAG_RESP);
Harald Welte33ec09b2018-02-10 15:34:46 +0100906
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100907 log("f_mt_call_complete 1");
908
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200909 cpars.got_osmux_count := 0;
910
Harald Welte33ec09b2018-02-10 15:34:46 +0100911 /* MS <- MSC: Expect CC SETUP */
912 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_SETUP(cpars.transaction_id, *, cpars.called_party)));
913
914 /* MS -> MSC: ALERTING */
915 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_ALERTING(cpars.transaction_id)));
916 MNCC.receive(tr_MNCC_ALERT_ind(cpars.mncc_callref));
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100917 log("f_mt_call_complete 2");
Harald Welte33ec09b2018-02-10 15:34:46 +0100918
Harald Welte33ec09b2018-02-10 15:34:46 +0100919 /* Create MGCP expect */
920 f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit});
921 /* Ask MSC via MNCC to create the RTP socket on the MSC/MGW side */
922 MNCC.send(ts_MNCC_RTP_CREATE(cpars.mncc_callref));
923
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200924 /* First MGCP CRCX */
Harald Welte33ec09b2018-02-10 15:34:46 +0100925 MGCP.receive(tr_CRCX) -> value mgcp_cmd {
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100926 log("f_mt_call_complete 3");
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200927 if (not f_handle_crcx(cpars, mgcp_cmd)) {
928 return;
Philipp Maier4b2692d2018-03-14 16:37:48 +0100929 }
Harald Welte33ec09b2018-02-10 15:34:46 +0100930 }
Neels Hofmeyr59e0c6f2019-03-06 15:21:20 +0100931
Neels Hofmeyr59e0c6f2019-03-06 15:21:20 +0100932
Harald Welte9b751a62019-04-14 17:39:29 +0200933 if (g_pars.ran_is_geran) {
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200934 var template BSSMAP_IE_AoIP_TransportLayerAddress tla_ass :=
Pau Espin Pedrol833174e2020-09-03 16:46:02 +0200935 f_tr_BSSMAP_IE_AoIP_TLA(cpars.mgw_conn_1.mgw_rtp_ip, ?);
Harald Welte9b751a62019-04-14 17:39:29 +0200936
937 interleave {
938 /* Second MGCP CRCX (this time for MSS/CN side) */
939 [] MGCP.receive(tr_CRCX(cpars.mgcp_ep)) -> value mgcp_cmd {
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100940 log("f_mt_call_complete 4");
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200941 if (not f_handle_crcx(cpars, mgcp_cmd)) {
942 break;
943 }
Harald Welte9b751a62019-04-14 17:39:29 +0200944 }
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100945
946 /* MSC acknowledges the MNCC_CREATE to the MNCC handler */
947 [] MNCC.receive(tr_MNCC_RTP_CREATE(cpars.mncc_callref)) {
948 log("f_mt_call_complete 5");
949 }
950
Harald Welte9b751a62019-04-14 17:39:29 +0200951 /* expect the MSC to trigger a BSSMAP ASSIGNMENT */
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200952 [] BSSAP.receive(tr_BSSMAP_AssignmentReq(omit, tla_ass)) -> value bssap {
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200953 var template BSSMAP_IE_AoIP_TransportLayerAddress tla;
Harald Welte9b751a62019-04-14 17:39:29 +0200954 var BSSMAP_IE_SpeechCodec codec;
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200955 var BSSMAP_IE_Osmo_OsmuxCID osmuxCID;
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100956 log("f_mt_call_complete 6");
Pau Espin Pedrol833174e2020-09-03 16:46:02 +0200957 tla := f_ts_BSSMAP_IE_AoIP_TLA(cpars.bss_rtp_ip, cpars.bss_rtp_port);
Harald Welte9b751a62019-04-14 17:39:29 +0200958 codec := valueof(ts_BSSMAP_IE_SpeechCodec({ts_CodecFR}));
959
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200960 if (cpars.use_osmux) {
961 if (not ispresent(bssap.pdu.bssmap.assignmentRequest.osmuxCID)) {
962 setverdict(fail, "MSC sent AssignReq without expected OsmuxCID IE");
963 mtc.stop;
964 }
965 osmuxCID := valueof(ts_OsmuxCID(0));
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +0200966 if (not match(bssap.pdu.bssmap.assignmentRequest.osmuxCID, osmuxCID)) {
967 setverdict(fail, "MSC sent AssignReq without expected OsmuxCID IE. Expected ", osmuxCID, " Got ", bssap.pdu.bssmap.assignmentRequest.osmuxCID);
Pau Espin Pedrola65697d2019-05-21 12:54:39 +0200968 mtc.stop;
969 }
970 bssap := valueof(ts_BSSMAP_AssignmentComplete(omit, tla, codec, osmuxCID));
971 } else {
972 bssap := valueof(ts_BSSMAP_AssignmentComplete(omit, tla, codec));
973 }
974 BSSAP.send(bssap);
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100975
976 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_CONNECT(cpars.transaction_id)));
Harald Welte9b751a62019-04-14 17:39:29 +0200977 }
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100978
979 [] MNCC.receive(tr_MNCC_SETUP_cnf(cpars.mncc_callref)) {
980 log("f_mt_call_complete 7");
Pau Espin Pedrol563b3d02020-09-09 20:19:52 +0200981 var octetstring ip;
982 var boolean is_ipv6 := f_addr_is_ipv6(cpars.mncc_rtp_ip);
983 if (is_ipv6) {
984 ip := f_inet6_addr(cpars.mncc_rtp_ip);
985 } else {
986 ip := f_inet_addr(cpars.mncc_rtp_ip);
987 }
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100988 MNCC.send(ts_MNCC_RTP_CONNECT(cpars.mncc_callref,
Pau Espin Pedrol563b3d02020-09-09 20:19:52 +0200989 is_ipv6, ip,
990 cpars.mncc_rtp_port,
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100991 /* payload type 3 = GSM FR */ 3));
992 }
993
994 /* MDCX setting up the RAN side remote RTP address received from Assignment Complete */
995 [] MGCP.receive(tr_MDCX) -> value mgcp_cmd {
996 log("f_mt_call_complete 8");
Pau Espin Pedrolcb4c59d2020-09-03 20:55:57 +0200997 var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_conn_1.mgw_rtp_ip, cpars.mgw_conn_1.mgw_rtp_ip,
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +0100998 hex2str(cpars.mgcp_call_id), "42",
Pau Espin Pedrolcb4c59d2020-09-03 20:55:57 +0200999 cpars.mgw_conn_1.mgw_rtp_port,
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001000 { int2str(cpars.rtp_payload_type) },
1001 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
1002 cpars.rtp_sdp_format)),
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001003 valueof(ts_SDP_ptime(20)) }));
1004 if (cpars.use_osmux) {
1005 osmux_cid := f_MgcpCmd_extract_osmux_cid(mgcp_cmd);
1006 if (osmux_cid != 0) { /* we expect MSC to use specific CID here */
1007 setverdict(fail, "MSC using unexpected CID " & int2str(osmux_cid) & " != 0");
1008 mtc.stop;
1009 }
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001010 mgcp_resp := ts_MDCX_ACK_osmux(mgcp_cmd.line.trans_id, cpars.mgw_conn_1.mgcp_connection_id, osmux_cid, sdp);
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001011 } else {
Pau Espin Pedrolcb4c59d2020-09-03 20:55:57 +02001012 mgcp_resp := ts_MDCX_ACK(mgcp_cmd.line.trans_id, cpars.mgw_conn_1.mgcp_connection_id, sdp);
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001013 }
1014 MGCP.send(mgcp_resp);
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001015 }
1016
1017 /* MDCX setting up the CN side remote RTP address received from MNCC CONNECT */
1018 [] MGCP.receive(tr_MDCX) -> value mgcp_cmd {
Pau Espin Pedrol956bfd22020-09-10 18:03:08 +02001019 var SDP_Message sdp;
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001020 log("f_mt_call_complete 9");
Pau Espin Pedrol956bfd22020-09-10 18:03:08 +02001021
1022 if (isvalue(mgcp_cmd.sdp)) {
1023 sdp := mgcp_cmd.sdp;
1024 if (sdp.media_list[0].media_field.ports.port_number != cpars.mncc_rtp_port) {
1025 setverdict(fail, "Wrong MDCX Connection port received, expected ", cpars.mncc_rtp_port, " and received ", sdp.media_list[0].media_field.ports.port_number)
1026 mtc.stop;
1027 }
1028 if (sdp.connection.conn_addr.addr != cpars.mncc_rtp_ip) {
1029 setverdict(fail, "Wrong MDCX Connection address received, expected ", cpars.mncc_rtp_ip, " and received ", sdp.connection.conn_addr.addr)
1030 mtc.stop;
1031 }
1032 }
1033
1034 sdp := valueof(ts_SDP(cpars.mgw_conn_2.mgw_rtp_ip, cpars.mgw_conn_2.mgw_rtp_ip,
1035 hex2str(cpars.mgcp_call_id), "42",
1036 cpars.mgw_conn_2.mgw_rtp_port,
1037 { int2str(cpars.rtp_payload_type) },
1038 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
1039 cpars.rtp_sdp_format)),
1040 valueof(ts_SDP_ptime(20)) }));
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001041 MGCP.send(ts_MDCX_ACK(mgcp_cmd.line.trans_id, cpars.mgw_conn_2.mgcp_connection_id, sdp));
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001042 }
1043
Neels Hofmeyr59e0c6f2019-03-06 15:21:20 +01001044 }
Harald Welte9b751a62019-04-14 17:39:29 +02001045 } else {
Harald Welte9b751a62019-04-14 17:39:29 +02001046 interleave {
1047 [] MGCP.receive(tr_CRCX(cpars.mgcp_ep)) -> value mgcp_cmd {
Neels Hofmeyr8fe8a902019-11-03 05:51:03 +01001048 log("f_mt_call_complete 4.iu");
1049 if (not f_handle_crcx(cpars, mgcp_cmd)) {
1050 break;
Harald Welte9b751a62019-04-14 17:39:29 +02001051 }
Harald Welte9b751a62019-04-14 17:39:29 +02001052 }
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001053
Neels Hofmeyr8fe8a902019-11-03 05:51:03 +01001054 /* MSC acknowledges the MNCC_CREATE to the MNCC handler */
1055 [] MNCC.receive(tr_MNCC_RTP_CREATE(cpars.mncc_callref)) {
1056 log("f_mt_call_complete 5.iu");
1057 }
1058
1059 [] BSSAP.receive(tr_RANAP_RabAssReq(?)) {
1060 log("f_mt_call_complete 6.iu");
1061 var RAB_SetupOrModifiedList l := {
1062 {
1063 {
1064 id := id_RAB_SetupOrModifiedItem,
1065 criticality := ignore,
1066 value_ := {
1067 rAB_SetupOrModifiedItem := {
1068 rAB_ID := int2bit(23, 8),
1069 transportLayerAddress := hex2bit( '350001c0a8021500000000000000000000000000'H),
1070 iuTransportAssociation := {
1071 bindingID := '040c0000'O
1072 },
1073 dl_dataVolumes := omit,
1074 iE_Extensions := omit
1075 }
1076 }
1077 }
1078 }
1079 };
1080 BSSAP.send(ts_RANAP_RabAssResp(l));
1081
1082 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_CONNECT(cpars.transaction_id)));
1083 }
1084
1085 [] MNCC.receive(tr_MNCC_SETUP_cnf(cpars.mncc_callref)) {
1086 log("f_mt_call_complete 7.iu");
Pau Espin Pedrol563b3d02020-09-09 20:19:52 +02001087 var octetstring ip;
1088 var boolean is_ipv6 := f_addr_is_ipv6(cpars.mncc_rtp_ip);
1089 if (is_ipv6) {
1090 ip := f_inet6_addr(cpars.mncc_rtp_ip);
1091 } else {
1092 ip := f_inet_addr(cpars.mncc_rtp_ip);
1093 }
Neels Hofmeyr8fe8a902019-11-03 05:51:03 +01001094 MNCC.send(ts_MNCC_RTP_CONNECT(cpars.mncc_callref,
Pau Espin Pedrol563b3d02020-09-09 20:19:52 +02001095 is_ipv6, ip,
1096 cpars.mncc_rtp_port,
Neels Hofmeyr8fe8a902019-11-03 05:51:03 +01001097 /* payload type 3 = GSM FR */ 3));
1098 }
1099
1100 /* MDCX setting up the RAN side remote RTP address received from Assignment Complete */
1101 [] MGCP.receive(tr_MDCX) -> value mgcp_cmd {
1102 log("f_mt_call_complete 8.iu");
Pau Espin Pedrolcb4c59d2020-09-03 20:55:57 +02001103 var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_conn_1.mgw_rtp_ip, cpars.mgw_conn_1.mgw_rtp_ip,
Neels Hofmeyr8fe8a902019-11-03 05:51:03 +01001104 hex2str(cpars.mgcp_call_id), "42",
Pau Espin Pedrolcb4c59d2020-09-03 20:55:57 +02001105 cpars.mgw_conn_1.mgw_rtp_port,
Neels Hofmeyr8fe8a902019-11-03 05:51:03 +01001106 { int2str(cpars.rtp_payload_type) },
1107 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
1108 cpars.rtp_sdp_format)),
1109 valueof(ts_SDP_ptime(20)) }));
1110 if (cpars.use_osmux) {
1111 osmux_cid := f_MgcpCmd_extract_osmux_cid(mgcp_cmd);
1112 if (osmux_cid != 0) { /* we expect MSC to use specific CID here */
1113 setverdict(fail, "MSC using unexpected CID " & int2str(osmux_cid) & " != 0");
1114 mtc.stop;
1115 }
1116 mgcp_resp := ts_MDCX_ACK_osmux(mgcp_cmd.line.trans_id, cpars.mgw_conn_1.mgcp_connection_id, osmux_cid, sdp);
1117 } else {
Pau Espin Pedrolcb4c59d2020-09-03 20:55:57 +02001118 mgcp_resp := ts_MDCX_ACK(mgcp_cmd.line.trans_id, cpars.mgw_conn_1.mgcp_connection_id, sdp);
Neels Hofmeyr8fe8a902019-11-03 05:51:03 +01001119 }
1120 MGCP.send(mgcp_resp);
1121 }
1122
1123 /* MDCX setting up the CN side remote RTP address received from MNCC CONNECT */
1124 [] MGCP.receive(tr_MDCX) -> value mgcp_cmd {
1125 log("f_mt_call_complete 9.iu");
1126 var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_conn_2.mgw_rtp_ip, cpars.mgw_conn_2.mgw_rtp_ip,
1127 hex2str(cpars.mgcp_call_id), "42",
1128 cpars.mgw_conn_2.mgw_rtp_port,
1129 { int2str(cpars.rtp_payload_type) },
1130 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
1131 cpars.rtp_sdp_format)),
1132 valueof(ts_SDP_ptime(20)) }));
1133 MGCP.send(ts_MDCX_ACK(mgcp_cmd.line.trans_id, cpars.mgw_conn_2.mgcp_connection_id, sdp));
1134 }
1135
Harald Welte33ec09b2018-02-10 15:34:46 +01001136 }
Neels Hofmeyr59e0c6f2019-03-06 15:21:20 +01001137 }
Harald Welte33ec09b2018-02-10 15:34:46 +01001138
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001139 if (cpars.use_osmux == (cpars.got_osmux_count != 0)) {
1140 log("Osmux ok: use_osmux = ", cpars.use_osmux, " got_osmux_count = ", cpars.got_osmux_count);
1141 } else {
1142 setverdict(fail, "Osmux failure: use_osmux = ", cpars.use_osmux, " got_osmux_count = ", cpars.got_osmux_count);
1143 mtc.stop;
1144 }
1145
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001146 log("f_mt_call_complete DONE");
Harald Welte4263c522018-12-06 11:56:27 +01001147}
1148
Harald Weltee035e3e2019-04-21 17:32:05 +02001149function f_expect_paging(boolean by_tmsi := true)
1150runs on BSC_ConnHdlr {
Harald Welte9b751a62019-04-14 17:39:29 +02001151 if (g_pars.ran_is_geran) {
1152 BSSAP.receive(tr_BSSMAP_Paging(g_pars.imsi));
1153 } else {
Harald Welte62113fc2019-05-09 13:04:02 +02001154 BSSAP.receive(tr_RANAP_Paging(cs_domain, imsi_hex2oct(g_pars.imsi)));
Harald Welte9b751a62019-04-14 17:39:29 +02001155 }
Harald Weltee035e3e2019-04-21 17:32:05 +02001156}
1157
Harald Welte4263c522018-12-06 11:56:27 +01001158function f_mt_call_establish(inout CallParameters cpars)
1159runs on BSC_ConnHdlr {
1160
1161 /* Initiate the call via MNCC */
1162 f_mt_call_initate(cpars);
1163
1164 /* BSC <- MSC: Expect paging. FIXME: By TMSI or not? */
Harald Welte6811d102019-04-14 22:23:14 +02001165 f_ran_register_imsi(g_pars.imsi, g_pars.tmsi);
Harald Weltee035e3e2019-04-21 17:32:05 +02001166 f_expect_paging()
Harald Welte4263c522018-12-06 11:56:27 +01001167
1168 /* Complete the call via BSSAP */
1169 f_mt_call_complete(cpars);
Harald Welte33ec09b2018-02-10 15:34:46 +01001170
Harald Welte33ec09b2018-02-10 15:34:46 +01001171 setverdict(pass);
1172}
1173
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001174/* Reply to a received CRCX with an OK (or the reply configured in cpars), using the given parameters.
1175 * Return true when an OK reply was sent, false otherwise.
1176 * Count occurence of Osmux, include Osmux parameters in the reply if necessary. */
1177function f_handle_crcx(inout CallParameters cpars, MgcpCommand mgcp_cmd)
1178runs on BSC_ConnHdlr
1179return boolean {
1180 var CrcxResponse conn := cpars.mgw_conn_1;
1181 if (cpars.got_crcx_count > 0) {
1182 conn := cpars.mgw_conn_2;
1183 }
1184 cpars.got_crcx_count := cpars.got_crcx_count + 1;
1185
1186 var MgcpMessage mgcp_msg := {
1187 command := mgcp_cmd
1188 }
1189 var template MgcpResponse mgcp_resp;
1190 var MgcpOsmuxCID osmux_cid;
1191 var MgcpCallId call_id := f_MgcpCmd_extract_call_id(mgcp_cmd);
1192 if (ispresent(cpars.mgcp_call_id)) {
1193 if (cpars.mgcp_call_id != call_id) {
1194 setverdict(fail, "CRCX contained unexpected call id. Expected:", cpars.mgcp_call_id, " got:", call_id);
1195 mtc.stop;
1196 }
1197 } else {
1198 cpars.mgcp_call_id := call_id;
1199 }
1200
1201 /* When the endpoint contains a wildcard we keep the endpoint
1202 * identifier we have set up in cpars. Otherwise we use the
1203 * endpoint name that the call agent has supplied */
1204 if (match(mgcp_cmd.line.ep, t_MGCP_EP_wildcard) == false) {
1205 cpars.mgcp_ep := mgcp_cmd.line.ep;
1206 }
1207
1208 if (conn.resp == -1) {
1209 /* Reply with error */
1210 var MgcpResponse mgcp_rsp := {
1211 line := {
1212 code := "542",
1213 trans_id := mgcp_cmd.line.trans_id,
1214 string := "FORCED_FAIL"
1215 },
1216 sdp := omit
1217
1218 }
1219 var MgcpParameter mgcp_rsp_param := {
1220 code := "Z",
1221 val := cpars.mgcp_ep
1222 };
1223 mgcp_rsp.params[0] := mgcp_rsp_param;
1224 MGCP.send(mgcp_rsp);
1225 return false;
1226 }
1227
1228 if (conn.resp == 0) {
1229 /* Do not reply at all */
1230 return false;
1231 }
1232
1233 if (conn.resp != 1) {
1234 setverdict(fail, "Unexpected value for cpars.mgw_conn_*.resp, expect -1, 0 or 1");
1235 mtc.stop;
1236 }
1237
1238 var SDP_Message sdp := valueof(ts_SDP(conn.mgw_rtp_ip, conn.mgw_rtp_ip,
1239 hex2str(cpars.mgcp_call_id), "42",
1240 conn.mgw_rtp_port,
1241 { int2str(cpars.rtp_payload_type) },
1242 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
1243 cpars.rtp_sdp_format)),
1244 valueof(ts_SDP_ptime(20)) }));
1245
1246 if (f_mgcp_contains_par(mgcp_msg, "X-OSMUX")) {
1247 if (not cpars.use_osmux) {
1248 setverdict(fail, "MSC sent X-Osmux parameter in MGCP, but not expecting any Osmux");
1249 mtc.stop;
1250 }
1251 cpars.got_osmux_count := cpars.got_osmux_count + 1;
1252 /* we expect MSC to use wildcard here, i.e. osmux_cid == -1 */
1253 osmux_cid := f_MgcpCmd_extract_osmux_cid(mgcp_cmd);
1254 log("f_handle_crcx(): got Osmux CID: ", osmux_cid);
1255 if (osmux_cid != -1) {
1256 setverdict(fail, "MSC using unexpected CID " & int2str(osmux_cid) & " != -1");
1257 mtc.stop;
1258 }
1259
1260 osmux_cid := 0;
1261 mgcp_resp := ts_CRCX_ACK_osmux(mgcp_cmd.line.trans_id, conn.mgcp_connection_id, osmux_cid, sdp);
1262 } else {
1263 mgcp_resp := ts_CRCX_ACK(mgcp_cmd.line.trans_id, conn.mgcp_connection_id, sdp);
1264 }
1265
1266 f_mgcp_par_append(mgcp_resp.params, ts_MgcpParSpecEP(cpars.mgcp_ep));
1267 MGCP.send(mgcp_resp);
1268 return true;
1269}
1270
1271
1272altstep as_optional_mgcp_crcx(CallParameters cpars) runs on BSC_ConnHdlr {
1273 var MgcpCommand mgcp_cmd;
1274 [] MGCP.receive(tr_CRCX) -> value mgcp_cmd {
1275 log("as_optional_mgcp_crcx: rx CRCX");
1276 f_handle_crcx(cpars, mgcp_cmd);
1277
1278 /* Without this 'repeat', the as_optional_mgcp_crcx() exits currently waiting interleaves as soon as an
1279 * CRCX is handled. */
1280 repeat;
1281 }
1282}
1283
1284private altstep as_optional_mgcp_mdcx(HostName mgw_rtp_ip, PortNumber mgw_rtp_port) runs on BSC_ConnHdlr {
1285 var MgcpCommand mgcp_cmd;
1286 [] MGCP.receive(tr_MDCX) -> value mgcp_cmd {
1287 log("as_optional_mgcp_mdcx: rx MDCX");
1288 log(mgcp_cmd);
1289 var charstring conn_id;
1290 var charstring rtp_payload_type;
1291 f_mgcp_find_param_entry(mgcp_cmd.params, "I", conn_id);
1292 rtp_payload_type := mgcp_cmd.sdp.media_list[0].media_field.fmts[0];
1293
1294 var SDP_Message sdp := valueof(ts_SDP(mgw_rtp_ip, mgw_rtp_ip,
1295 mgcp_cmd.sdp.origin.session_id, "42",
1296 mgw_rtp_port,
1297 { rtp_payload_type },
1298 { valueof(ts_SDP_ptime(20)) }));
1299 MGCP.send(ts_MDCX_ACK(mgcp_cmd.line.trans_id, str2hex(conn_id), sdp));
1300
1301 /* Without this 'repeat', currently active other interleave and alt series exit as soon as an MDCX is
1302 * handled. */
1303 repeat;
1304 }
1305}
1306
Neels Hofmeyr0b16bf12019-11-03 05:10:12 +01001307private altstep as_optional_mgcp_dlcx(CallParameters cpars) runs on BSC_ConnHdlr {
1308 var MgcpCommand mgcp_cmd;
1309 var boolean respond_to_dlcx := not (isbound(cpars.mgw_drop_dlcx) and valueof(cpars.mgw_drop_dlcx));
1310 [] MGCP.receive(tr_DLCX(?)) -> value mgcp_cmd {
1311 log("as_optional_mgcp_dlcx: rx MGCP DLCX");
1312 if (respond_to_dlcx) {
1313 MGCP.send(ts_DLCX_ACK2(mgcp_cmd.line.trans_id));
1314 }
1315 /* Without this 'repeat', currently active other interleave and alt series exit as soon as a
1316 * DLCX is handled. */
1317 repeat;
1318 }
1319}
1320
Philipp Maier3716a5e2018-03-21 15:53:35 +01001321function f_mo_call_establish(inout CallParameters cpars)
Harald Welteb71901a2018-01-26 19:16:05 +01001322runs on BSC_ConnHdlr {
1323
Harald Welteb71901a2018-01-26 19:16:05 +01001324 var MNCC_PDU mncc;
1325 var MgcpCommand mgcp_cmd;
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001326 var template MgcpResponse mgcp_resp;
Philipp Maier2a98a732018-03-19 16:06:12 +01001327 var boolean respond_to_dlcx;
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001328 var PDU_BSSAP bssap;
Neels Hofmeyr0b16bf12019-11-03 05:10:12 +01001329 var RANAP_PDU ranap;
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001330 var MgcpOsmuxCID osmux_cid;
Harald Welteb71901a2018-01-26 19:16:05 +01001331
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001332 cpars.mo_call := true;
1333
Harald Welte0bef21e2018-02-10 09:48:23 +01001334 if (cpars.emergency) {
Harald Welteb9e86fa2018-04-09 18:18:31 +02001335 f_establish_fully(EST_TYPE_EMERG_CALL);
Harald Welte0bef21e2018-02-10 09:48:23 +01001336 } else {
Harald Welteb9e86fa2018-04-09 18:18:31 +02001337 f_establish_fully(EST_TYPE_MO_CALL);
Harald Welte0bef21e2018-02-10 09:48:23 +01001338 }
Harald Welteb71901a2018-01-26 19:16:05 +01001339
1340 /* Create MNCC and MGCP expect */
1341 f_create_mncc_expect(hex2str(cpars.called_party));
1342 f_create_mgcp_expect(ExpectCriteria:{omit,omit,omit});
1343
Harald Welte0bef21e2018-02-10 09:48:23 +01001344 if (cpars.emergency) {
1345 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_EMERG_SETUP(cpars.transaction_id)));
1346 } else {
1347 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_SETUP(cpars.transaction_id, cpars.called_party)));
1348 }
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001349
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001350 if (cpars.stop_after_cc_setup) {
1351 return;
1352 }
1353
1354 var template BSSMAP_IE_AoIP_TransportLayerAddress tla_ass :=
Pau Espin Pedrol833174e2020-09-03 16:46:02 +02001355 f_tr_BSSMAP_IE_AoIP_TLA(cpars.mgw_conn_1.mgw_rtp_ip, ?);
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001356
1357 var default mdcx := activate(as_optional_mgcp_mdcx(cpars.mgw_conn_2.mgw_rtp_ip, cpars.mgw_conn_2.mgw_rtp_port));
Neels Hofmeyr8df69622019-11-02 19:16:03 +01001358 var boolean got_mncc_setup_compl_ind := false;
1359 var boolean got_cc_connect := false;
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001360
Harald Welteb71901a2018-01-26 19:16:05 +01001361 interleave {
1362 [] MNCC.receive(tr_MNCC_SETUP_ind(?, tr_MNCC_number(hex2str(cpars.called_party)))) -> value mncc {
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001363 log("f_mo_call_establish 1: rx MNCC SETUP ind");
Harald Welteb71901a2018-01-26 19:16:05 +01001364 cpars.mncc_callref := mncc.u.signal.callref;
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001365 MNCC.send(ts_MNCC_RTP_CREATE(cpars.mncc_callref));
1366 }
1367
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001368 /* First MGCP CRCX */
Harald Welteb71901a2018-01-26 19:16:05 +01001369 [] MGCP.receive(tr_CRCX) -> value mgcp_cmd {
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001370 log("f_mo_call_establish 2: rx 1st CRCX");
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001371 if (not f_handle_crcx(cpars, mgcp_cmd)) {
1372 break;
1373 }
Philipp Maierf1e02bb2018-03-15 16:30:00 +01001374 }
1375
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001376 /* Second MGCP CRCX */
1377 [] MGCP.receive(tr_CRCX(cpars.mgcp_ep)) -> value mgcp_cmd {
1378 log("f_mo_call_establish 6: rx 2nd CRCX");
1379 if (not f_handle_crcx(cpars, mgcp_cmd)) {
1380 break;
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001381 }
Harald Welteb71901a2018-01-26 19:16:05 +01001382 }
Neels Hofmeyr59e0c6f2019-03-06 15:21:20 +01001383
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001384 [] MNCC.receive(tr_MNCC_RTP_CREATE(cpars.mncc_callref)) {
1385 log("f_mo_call_establish 3: rx RTP CREATE");
1386 /* Call Proceeding */
1387 MNCC.send(ts_MNCC_CALL_PROC_req(cpars.mncc_callref, cpars.mncc_bearer_cap));
1388 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_CALL_PROC(cpars.transaction_id)));
Harald Welteb71901a2018-01-26 19:16:05 +01001389
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001390 /* Alerting */
1391 MNCC.send(ts_MNCC_ALERT_req(cpars.mncc_callref));
Neels Hofmeyr59e0c6f2019-03-06 15:21:20 +01001392 }
Harald Welte9b751a62019-04-14 17:39:29 +02001393
Neels Hofmeyreccaa992019-10-15 16:42:21 +02001394 //[g_pars.ran_is_geran] BSSAP.receive(tr_BSSMAP_AssignmentReq(omit, tla_ass)) -> value bssap
Neels Hofmeyrd9d835d2019-09-17 18:19:08 +02001395 [] BSSAP.receive(tr_BSSMAP_AssignmentReq(omit)) -> value bssap {
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001396 log("f_mo_call_establish 4: rx Assignment Request");
1397 var BSSMAP_IE_AoIP_TransportLayerAddress tla;
1398 var BSSMAP_IE_SpeechCodec codec;
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001399 var BSSMAP_IE_Osmo_OsmuxCID osmuxCID;
1400
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001401 if (not match(bssap.pdu.bssmap.assignmentRequest.aoIPTransportLayer, tla_ass)) {
Neels Hofmeyrd9d835d2019-09-17 18:19:08 +02001402 log("Expected:", tla_ass);
1403 log("Got:", bssap.pdu.bssmap.assignmentRequest.aoIPTransportLayer);
1404 setverdict(fail, "MSC sent Assignment Request with unexpected AoIP Transport Layer IE");
1405 mtc.stop;
1406 }
1407
Pau Espin Pedrol833174e2020-09-03 16:46:02 +02001408 tla := valueof(f_ts_BSSMAP_IE_AoIP_TLA(cpars.bss_rtp_ip, cpars.bss_rtp_port));
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001409 codec := valueof(ts_BSSMAP_IE_SpeechCodec({ts_CodecFR}));
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001410 if (cpars.use_osmux) {
1411 if (not ispresent(bssap.pdu.bssmap.assignmentRequest.osmuxCID)) {
1412 setverdict(fail, "MSC sent AssignReq without expected OsmuxCID IE");
1413 mtc.stop;
1414 }
1415 osmuxCID := valueof(ts_OsmuxCID(0));
1416 if (cpars.use_osmux and not match(bssap.pdu.bssmap.assignmentRequest.osmuxCID, osmuxCID)) {
1417 setverdict(fail, "MSC sent AssignReq without expected OsmuxCID IE");
1418 mtc.stop;
1419 }
1420 bssap := valueof(ts_BSSMAP_AssignmentComplete(omit, tla, codec, osmuxCID));
1421 } else {
1422 bssap := valueof(ts_BSSMAP_AssignmentComplete(omit, tla, codec));
1423 }
1424 BSSAP.send(bssap);
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001425 }
Neels Hofmeyr0b16bf12019-11-03 05:10:12 +01001426 [] BSSAP.receive(tr_RANAP_RabAssReq(*)) -> value ranap {
1427 log("f_mo_call_establish 4.iu: rx RANAP RAB Assignment Request");
1428 var RAB_SetupOrModifiedList l := {
1429 {
1430 {
1431 id := id_RAB_SetupOrModifiedItem,
1432 criticality := ignore,
1433 value_ := {
1434 rAB_SetupOrModifiedItem := {
1435 rAB_ID := int2bit(23, 8),
1436 transportLayerAddress := hex2bit( '350001c0a8021500000000000000000000000000'H),
1437 iuTransportAssociation := {
1438 bindingID := '040c0000'O
1439 },
1440 dl_dataVolumes := omit,
1441 iE_Extensions := omit
1442 }
1443 }
1444 }
1445 }
1446 };
1447 BSSAP.send(ts_RANAP_RabAssResp(l));
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001448 }
Harald Welte9b751a62019-04-14 17:39:29 +02001449
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001450 /* MDCX setting up the RAN side remote RTP address received from Assignment Complete */
1451 [] MGCP.receive(tr_MDCX) -> value mgcp_cmd {
1452 log("f_mo_call_establish 5: rx MDCX for the RAN side");
Pau Espin Pedrolcb4c59d2020-09-03 20:55:57 +02001453 var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_conn_1.mgw_rtp_ip, cpars.mgw_conn_1.mgw_rtp_ip,
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001454 hex2str(cpars.mgcp_call_id), "42",
Pau Espin Pedrolcb4c59d2020-09-03 20:55:57 +02001455 cpars.mgw_conn_1.mgw_rtp_port,
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001456 { int2str(cpars.rtp_payload_type) },
1457 { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type,
1458 cpars.rtp_sdp_format)),
1459 valueof(ts_SDP_ptime(20)) }));
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001460
1461 if (cpars.use_osmux) {
1462 osmux_cid := f_MgcpCmd_extract_osmux_cid(mgcp_cmd);
1463 if (osmux_cid != 0) { /* we expect MSC to use specific CID here */
1464 setverdict(fail, "MSC using unexpected CID " & int2str(osmux_cid) & " != 0");
1465 mtc.stop;
1466 }
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001467 mgcp_resp := ts_MDCX_ACK_osmux(mgcp_cmd.line.trans_id, cpars.mgw_conn_1.mgcp_connection_id, osmux_cid, sdp);
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001468 } else {
Pau Espin Pedrolcb4c59d2020-09-03 20:55:57 +02001469 mgcp_resp := ts_MDCX_ACK(mgcp_cmd.line.trans_id, cpars.mgw_conn_1.mgcp_connection_id, sdp);
Pau Espin Pedrola65697d2019-05-21 12:54:39 +02001470 }
1471 MGCP.send(mgcp_resp);
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001472 }
Harald Welte9b751a62019-04-14 17:39:29 +02001473
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001474 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_ALERTING(cpars.transaction_id))) {
1475 log("f_mo_call_establish 7: rx CC Alerting");
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001476
1477 if (cpars.ran_clear_when_alerting) {
1478 if (g_pars.ran_is_geran) {
1479 BSSAP.send(ts_BSSMAP_ClearRequest(0));
1480 } else {
1481 BSSAP.send(ts_RANAP_IuReleaseRequest(ts_RanapCause_om_intervention));
1482 }
1483 break;
1484 }
1485
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001486 cpars.mncc_callref := mncc.u.signal.callref;
1487 /* Call Proceeding */
Pau Espin Pedrol563b3d02020-09-09 20:19:52 +02001488 var octetstring ip;
1489 var boolean is_ipv6 := f_addr_is_ipv6(cpars.mncc_rtp_ip);
1490 if (is_ipv6) {
1491 ip := f_inet6_addr(cpars.mncc_rtp_ip);
1492 } else {
1493 ip := f_inet_addr(cpars.mncc_rtp_ip);
1494 }
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001495 MNCC.send(ts_MNCC_RTP_CONNECT(cpars.mncc_callref,
Pau Espin Pedrol563b3d02020-09-09 20:19:52 +02001496 is_ipv6, ip,
1497 cpars.mncc_rtp_port,
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001498 /* payload type 3 = GSM FR */ 3));
1499 MNCC.send(ts_MNCC_SETUP_rsp(cpars.mncc_callref));
1500 }
1501
1502 [] MNCC.receive(tr_MNCC_SETUP_COMPL_ind(?)) -> value mncc {
1503 log("f_mo_call_establish 8: rx MNCC SETUP COMPLETE ind");
Neels Hofmeyr8df69622019-11-02 19:16:03 +01001504 got_mncc_setup_compl_ind := true;
1505 if (not cpars.expect_release and got_cc_connect) {
1506 break;
1507 }
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001508 }
1509
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001510 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_CONNECT(cpars.transaction_id))) {
1511 log("f_mo_call_establish 10: rx CC CONNECT");
1512 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_CONNECT_ACK(cpars.transaction_id)));
Neels Hofmeyr8df69622019-11-02 19:16:03 +01001513 got_cc_connect := true;
1514 if (not cpars.expect_release and got_mncc_setup_compl_ind) {
1515 break;
1516 }
Harald Welteb71901a2018-01-26 19:16:05 +01001517 }
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001518
1519 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_RELEASE(cpars.transaction_id))) {
1520 log("f_mo_call_establish 11: rx CC RELEASE");
Neels Hofmeyr8df69622019-11-02 19:16:03 +01001521 if (not cpars.expect_release) {
1522 setverdict(fail, "Got unexpected CC Release");
1523 mtc.stop;
1524 }
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001525 f_expect_clear();
1526 break;
1527 }
1528 }
1529
1530 f_sleep(0.5);
1531 deactivate(mdcx);
1532
1533 if (cpars.use_osmux == (cpars.got_osmux_count != 0)) {
1534 log("Osmux ok: use_osmux = ", cpars.use_osmux, " got_osmux_count = ", cpars.got_osmux_count);
1535 } else {
1536 setverdict(fail, "Osmux failure: use_osmux = ", cpars.use_osmux, " got_osmux_count = ", cpars.got_osmux_count);
1537 mtc.stop;
Harald Welteb71901a2018-01-26 19:16:05 +01001538 }
1539
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001540 log("f_mo_call_establish DONE");
Philipp Maier3716a5e2018-03-21 15:53:35 +01001541 setverdict(pass);
1542}
Harald Welteb71901a2018-01-26 19:16:05 +01001543
Harald Welte4c422b72019-02-17 16:27:10 +01001544function f_call_hangup(inout CallParameters cpars, boolean release_by_ms, boolean is_csfb := false)
Philipp Maier3716a5e2018-03-21 15:53:35 +01001545runs on BSC_ConnHdlr {
1546
1547 var MobileIdentityLV mi;
1548 var MNCC_PDU mncc;
1549 var MgcpCommand mgcp_cmd;
1550 var boolean respond_to_dlcx;
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001551 var boolean dlcx_contained_ci := false;
1552 var template PDU_BSSAP t_clear := tr_BSSMAP_ClearCommand;
1553
1554 if (is_csfb) {
1555 t_clear := tr_BSSMAP_ClearCommandCSFB;
1556 }
1557
Neels Hofmeyrba960a12019-11-02 19:17:38 +01001558 log("f_call_hangup 0: tx MNCC_DISC_REQ");
Harald Welteb71901a2018-01-26 19:16:05 +01001559 MNCC.send(ts_MNCC_DISC_req(cpars.mncc_callref, valueof(ts_MNCC_cause(23))));
1560 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_DISC(cpars.transaction_id)));
1561
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001562 log("f_call_hangup 1: rx DTAP CC DISC");
1563
Philipp Maier3716a5e2018-03-21 15:53:35 +01001564 if (release_by_ms) {
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001565 var BIT1 tid_remote := '1'B;
1566 if (cpars.mo_call) {
1567 tid_remote := '0'B;
1568 }
Philipp Maier3716a5e2018-03-21 15:53:35 +01001569 /* B-side (MS) Release of call */
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001570 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_RELEASE(cpars.transaction_id, tid_remote, '0000000'B)));
Philipp Maier3716a5e2018-03-21 15:53:35 +01001571 MNCC.receive(tr_MNCC_REL_ind(cpars.mncc_callref));
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001572 log("f_call_hangup 2: rx MNCC REL ind");
Philipp Maier3716a5e2018-03-21 15:53:35 +01001573 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_REL_COMPL(cpars.transaction_id)));
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001574 log("f_call_hangup 3: rx DTAP CC REL COMPL");
Philipp Maier3716a5e2018-03-21 15:53:35 +01001575 } else {
1576 /* A-side (PLMN) Release of call */
1577 MNCC.send(ts_MNCC_REL_req(cpars.mncc_callref, valueof(ts_MNCC_cause(42))));
1578 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_CC_RELEASE(cpars.transaction_id)));
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001579 log("f_call_hangup 4: rx DTAP CC RELEASE");
Philipp Maier3716a5e2018-03-21 15:53:35 +01001580 BSSAP.send(ts_PDU_DTAP_MO(ts_ML3_MO_CC_REL_COMPL(cpars.transaction_id)));
1581 }
Harald Welteb71901a2018-01-26 19:16:05 +01001582
Philipp Maier2a98a732018-03-19 16:06:12 +01001583 respond_to_dlcx := not (isbound(cpars.mgw_drop_dlcx) and valueof(cpars.mgw_drop_dlcx));
1584
Neels Hofmeyr3c89a6b2019-10-15 16:54:37 +02001585 var default mdcx := activate(as_optional_mgcp_mdcx(cpars.mgw_conn_2.mgw_rtp_ip, cpars.mgw_conn_2.mgw_rtp_port));
Neels Hofmeyr0b16bf12019-11-03 05:10:12 +01001586 var default dlcx := activate(as_optional_mgcp_dlcx(cpars));
Harald Welte9b751a62019-04-14 17:39:29 +02001587
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001588 /* clearing of radio channel */
Neels Hofmeyr0b16bf12019-11-03 05:10:12 +01001589 alt {
1590 [g_pars.ran_is_geran] BSSAP.receive(t_clear) {
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001591 log("f_call_hangup 5: rx BSSAP Clear Command");
1592 BSSAP.send(ts_BSSMAP_ClearComplete);
1593 BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_IND);
1594 log("f_call_hangup 6: rx SCCP DISC");
Neels Hofmeyr0b16bf12019-11-03 05:10:12 +01001595 setverdict(pass);
Philipp Maier2a98a732018-03-19 16:06:12 +01001596 }
Neels Hofmeyr0b16bf12019-11-03 05:10:12 +01001597 [not g_pars.ran_is_geran] BSSAP.receive(tr_RANAP_IuReleaseCommand(?)) {
1598 log("f_call_hangup 5.iu: rx Iu Release Command");
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001599 BSSAP.send(ts_RANAP_IuReleaseComplete);
1600 BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_IND);
Neels Hofmeyr0b16bf12019-11-03 05:10:12 +01001601 log("f_call_hangup 6.iu: rx SCCP DISC");
1602 setverdict(pass);
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001603 }
1604 }
1605
Neels Hofmeyr0b16bf12019-11-03 05:10:12 +01001606 f_sleep(1.0);
Neels Hofmeyr2ca1ab42019-03-08 03:45:43 +01001607 f_create_mgcp_delete_ep(cpars.mgcp_ep);
1608 log("f_call_hangup 9: done");
1609
1610 deactivate(mdcx);
Neels Hofmeyr0b16bf12019-11-03 05:10:12 +01001611 deactivate(dlcx);
Philipp Maier3716a5e2018-03-21 15:53:35 +01001612}
1613
1614function f_mt_call(inout CallParameters cpars)
1615runs on BSC_ConnHdlr {
1616
1617 f_mt_call_establish(cpars);
1618
Neels Hofmeyrba960a12019-11-02 19:17:38 +01001619 log("Hold the call for some time");
Philipp Maier3716a5e2018-03-21 15:53:35 +01001620 f_sleep(3.0);
1621
Neels Hofmeyrba960a12019-11-02 19:17:38 +01001622 log("Hangup");
Philipp Maier3716a5e2018-03-21 15:53:35 +01001623 f_call_hangup(cpars, true);
1624
1625 setverdict(pass);
1626}
1627
1628function f_mo_call(inout CallParameters cpars)
1629runs on BSC_ConnHdlr {
1630
1631 f_mo_call_establish(cpars);
1632
Neels Hofmeyrba960a12019-11-02 19:17:38 +01001633 log("Hold the call for some time");
Philipp Maier3716a5e2018-03-21 15:53:35 +01001634 f_sleep(3.0);
1635
Neels Hofmeyrba960a12019-11-02 19:17:38 +01001636 log("Hangup");
Philipp Maier3716a5e2018-03-21 15:53:35 +01001637 f_call_hangup(cpars, false);
1638
Harald Welteb71901a2018-01-26 19:16:05 +01001639 setverdict(pass);
1640}
Harald Weltea49e36e2018-01-21 19:29:33 +01001641
Daniel Willmann8b084372018-02-04 13:35:26 +01001642function f_mo_seq_dtmf_dup(inout CallParameters cpars)
1643runs on BSC_ConnHdlr {
1644
1645 timer T := 1.0;
Daniel Willmann8b084372018-02-04 13:35:26 +01001646 var MNCC_PDU mncc;
1647 var MgcpCommand mgcp_cmd;
1648 var template PDU_ML3_MS_NW dtmf_dtap;
1649
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001650 f_mo_call_establish(cpars);
Daniel Willmann8b084372018-02-04 13:35:26 +01001651
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001652 /* Send DTMF: send the exact same DTAP message twice, the dup should be filtered out by
1653 * 3GPP TS 24.007 11.2.3.2 Message Type Octet / Duplicate Detection. */
Daniel Willmann8b084372018-02-04 13:35:26 +01001654
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001655 /* Find out the next NSD that will be used, from RAN emulation. */
1656 var N_Sd_Array last_n_sd := f_bssmap_last_n_sd();
1657 var uint2_t next_n_sd := f_next_n_sd(last_n_sd, 0 /* cc is index 0 */);
Daniel Willmann8b084372018-02-04 13:35:26 +01001658
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001659 /* Compose DTAP with this correct NSD */
Daniel Willmann8b084372018-02-04 13:35:26 +01001660 dtmf_dtap := ts_ML3_MO_CC_START_DTMF(cpars.transaction_id, "2");
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001661
1662 /* Here, pass skip_seq_patching == false so that the RAN Emulation NSD increments after this message. */
1663 BSSAP.send(ts_PDU_DTAP_MO(dtmf_dtap, '00'O, false));
Daniel Willmann8b084372018-02-04 13:35:26 +01001664 T.start;
1665 alt {
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001666 [] MNCC.receive(tr_MNCC_START_DTMF_ind(cpars.mncc_callref, "2")) {
1667 log("f_mo_seq_dtmf_dup() 1: got first START_DTMF_ind");
1668 }
Daniel Willmann8b084372018-02-04 13:35:26 +01001669 [] T.timeout {
1670 setverdict(fail, "Timeout waiting for START_DTMF_ind");
Daniel Willmannafce8662018-07-06 23:11:32 +02001671 mtc.stop;
Daniel Willmann8b084372018-02-04 13:35:26 +01001672 }
1673 }
1674
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001675 /* Send the exact same DTAP with above NSD, which is now incorrect (has not incremented), so that this message
1676 * will get filtered by the duplicate detection. Write NSD into DTAP and pass skip_seq_patching == true. */
1677 dtmf_dtap.msgs.cc.startDTMF.nsd := int2bit(next_n_sd, 2);
Daniel Willmann92f66272018-02-06 15:50:52 +01001678 BSSAP.send(ts_PDU_DTAP_MO(dtmf_dtap, '00'O, true));
Daniel Willmann8b084372018-02-04 13:35:26 +01001679 T.start;
1680 alt {
1681 [] MNCC.receive(tr_MNCC_START_DTMF_ind(cpars.mncc_callref, "2")) {
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001682 setverdict(fail, "f_mo_seq_dtmf_dup() 2: Received duplicate START_DTMF_ind");
Daniel Willmannafce8662018-07-06 23:11:32 +02001683 mtc.stop;
Daniel Willmann8b084372018-02-04 13:35:26 +01001684 }
1685 [] T.timeout { }
1686 }
1687
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001688 /* Here the NSD should be correct again and we see a DTMF. */
Daniel Willmann8b084372018-02-04 13:35:26 +01001689 dtmf_dtap := ts_ML3_MO_CC_START_DTMF(cpars.transaction_id, "3");
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001690 BSSAP.send(ts_PDU_DTAP_MO(dtmf_dtap, '00'O, false));
Daniel Willmann8b084372018-02-04 13:35:26 +01001691 alt {
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001692 [] MNCC.receive(tr_MNCC_START_DTMF_ind(cpars.mncc_callref, "3")) {
1693 log("f_mo_seq_dtmf_dup() 3: got second START_DTMF_ind");
1694 }
Daniel Willmann8b084372018-02-04 13:35:26 +01001695 [] T.timeout {
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001696 setverdict(fail, "Timeout waiting for final START_DTMF_ind");
Daniel Willmannafce8662018-07-06 23:11:32 +02001697 mtc.stop;
Daniel Willmann8b084372018-02-04 13:35:26 +01001698 }
1699 }
1700
Neels Hofmeyr3d22e4a2019-10-03 04:07:47 +02001701 f_call_hangup(cpars, true);
Daniel Willmann8b084372018-02-04 13:35:26 +01001702 setverdict(pass);
1703}
Harald Welte9b751a62019-04-14 17:39:29 +02001704
Harald Welte1ddc7162018-01-27 14:25:46 +01001705/* expect a clear command */
Harald Welte5946b332018-03-18 23:32:21 +01001706altstep as_clear_cmd_compl_disc(float t := 5.0) runs on BSC_ConnHdlr {
Neels Hofmeyr6b468a42019-03-06 15:25:14 +01001707 var PDU_BSSAP bssap;
Harald Welte5946b332018-03-18 23:32:21 +01001708 [] BSSAP.receive(tr_BSSMAP_ClearCommand) {
1709 BSSAP.send(ts_BSSMAP_ClearComplete);
1710 alt {
Harald Welte6811d102019-04-14 22:23:14 +02001711 [] BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {
Harald Welte5946b332018-03-18 23:32:21 +01001712 setverdict(pass);
1713 }
1714 [] BSSAP.receive {
1715 setverdict(fail, "Unexpected BSSMAP while waiting for SCCP Release");
Daniel Willmannafce8662018-07-06 23:11:32 +02001716 mtc.stop;
Harald Welte5946b332018-03-18 23:32:21 +01001717 }
1718 }
1719 }
Neels Hofmeyr6b468a42019-03-06 15:25:14 +01001720 [] BSSAP.receive(tr_BSSAP_BSSMAP) -> value bssap {
1721 setverdict(fail, "Unexpected BSSMAP while waiting for ClearCommand", bssap);
Daniel Willmannafce8662018-07-06 23:11:32 +02001722 mtc.stop;
Harald Welte5946b332018-03-18 23:32:21 +01001723 }
1724}
1725
Neels Hofmeyr4e18ccc2020-06-24 19:08:17 +02001726function f_expect_clear(float t := 5.0, boolean verify_vlr_cell_id := true) runs on BSC_ConnHdlr {
Harald Welte1ddc7162018-01-27 14:25:46 +01001727 timer T := t;
1728
1729 T.start;
1730 alt {
Harald Welte9b751a62019-04-14 17:39:29 +02001731 [g_pars.ran_is_geran] as_clear_cmd_compl_disc(t) { }
1732 [not g_pars.ran_is_geran] as_iu_release_compl_disc(t) { }
Harald Welte1ddc7162018-01-27 14:25:46 +01001733 [] T.timeout {
Harald Welte5946b332018-03-18 23:32:21 +01001734 setverdict(fail, "Timeout waiting for ClearCommand/Release");
Daniel Willmannafce8662018-07-06 23:11:32 +02001735 mtc.stop;
Harald Welte1ddc7162018-01-27 14:25:46 +01001736 }
1737 }
Neels Hofmeyr4e18ccc2020-06-24 19:08:17 +02001738
1739 if (verify_vlr_cell_id) {
1740 /* Now the conn is gone, but the VLR reflects the cell ID */
1741 f_verify_vty_lac_ci();
1742 }
Harald Welte1ddc7162018-01-27 14:25:46 +01001743}
1744
Neels Hofmeyr0ac63152019-05-07 01:20:17 +02001745function f_create_bssmap_exp_handoverRequest(integer targetPointCode) runs on BSC_ConnHdlr {
1746 BSSAP_PROC.call(RAN_register_handoverRequest:{targetPointCode, self}) {
1747 [] BSSAP_PROC.getreply(RAN_register_handoverRequest:{?, ?}) {};
1748 }
1749}
1750
1751function f_bssmap_last_n_sd() runs on BSC_ConnHdlr return N_Sd_Array {
1752 var N_Sd_Array last_n_sd;
1753 BSSAP_PROC.call(RAN_last_n_sd:{self, -}) {
1754 [] BSSAP_PROC.getreply(RAN_last_n_sd:{self, ?}) -> param(last_n_sd) {
1755 return last_n_sd;
1756 };
1757 }
1758}
1759
1760function f_bssmap_continue_after_n_sd(N_Sd_Array last_n_sd) runs on BSC_ConnHdlr {
1761 BSSAP_PROC.call(RAN_continue_after_n_sd:{last_n_sd, self}) {
1762 [] BSSAP_PROC.getreply(RAN_continue_after_n_sd:{last_n_sd, self});
1763 }
1764}
1765
Harald Weltef45efeb2018-04-09 18:19:24 +02001766type record SmsParametersTp {
1767 OCT1 msg_ref,
1768 TP_DA da,
1769 OCT1 pid,
1770 OCT1 dcs,
1771 integer udl,
1772 octetstring ud
1773}
1774type record SmsParametersRp {
1775 OCT1 msg_ref,
Vadim Yanitskiy437b5a62019-12-15 14:13:39 +09001776 RP_NumberingPlan_and_NumberDigits smsc_addr optional
Harald Weltef45efeb2018-04-09 18:19:24 +02001777}
1778type record SmsParameters {
1779 SmsParametersTp tp,
1780 SmsParametersRp rp,
1781 uint3_t tid,
1782 OCT1 dlci,
1783 uint7_t exp_rp_err optional
1784}
1785
1786template (value) TP_DA ts_TP_DA(BIT4 npl, BIT3 ton, hexstring addr) := {
1787 tP_DA_NoPad := {
1788 tP_LengthIndicator := 0, /* overwritten */
1789 tP_NumberingPlanID := npl,
1790 tP_TypeOfNumber := ton,
1791 tp_Spare := '0'B,
1792 tP_DAValue := addr
1793 }
1794}
1795
Vadim Yanitskiy437b5a62019-12-15 14:13:39 +09001796template RP_NumberingPlan_and_NumberDigits t_RP_Addr(template hexstring addr,
1797 template BIT4 npi := '0001'B,
1798 template BIT3 ton := '001'B,
1799 template BIT1 ext := '1'B) := {
1800 rP_NumberingPlanIdentification := npi,
1801 rP_TypeOfNumber := ton,
1802 rP_Ext := ext,
1803 rP_NumberDigits := addr
1804}
1805
Harald Weltef45efeb2018-04-09 18:19:24 +02001806template (value) SmsParameters t_SmsPars(hexstring tp_daddr := '12345'H) := {
1807 tp := {
1808 msg_ref := '23'O,
1809 da := ts_TP_DA('0000'B, '000'B, tp_daddr),
1810 pid := '00'O,
1811 dcs := '00'O,
1812 udl := 0,
1813 ud := ''O
1814 },
1815 rp := {
1816 msg_ref := '42'O,
Vadim Yanitskiy437b5a62019-12-15 14:13:39 +09001817 /* We don't really need to have both SM-RP-DA/OA here, because only one IE
Vadim Yanitskiyafb15a82020-01-05 14:04:53 +01001818 * is included in MO/MT SMS, and in the most cases it's the SMSC address.
1819 * NOTE: this address is currently hard-coded by OsmoMSC. */
1820 smsc_addr := t_RP_Addr('447785016005'H)
Harald Weltef45efeb2018-04-09 18:19:24 +02001821 },
1822 tid := 0,
1823 dlci := '03'O,
1824 exp_rp_err := omit
1825}
1826
1827private altstep as_other_sms() runs on BSC_ConnHdlr {
1828 [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_SMS(?, ?, ?), ?)) {
1829 setverdict(fail, "Unexpected SMS related PDU from MSC");
Daniel Willmannafce8662018-07-06 23:11:32 +02001830 mtc.stop;
Harald Weltef45efeb2018-04-09 18:19:24 +02001831 }
1832}
1833
Vadim Yanitskiy9cc019a2018-11-15 02:06:07 +07001834/* Submit a MO RP-SMMA over an already existing DTAP connection */
1835function f_mo_smma(inout SmsParameters spars)
1836runs on BSC_ConnHdlr {
1837 var template (value) RPDU_MS_SGSN rp_mo;
1838 var template (value) PDU_ML3_MS_NW l3_mo;
1839
1840 var default d := activate(as_other_sms());
1841
1842 /* just in case this is routed to SMPP.. */
1843 f_create_smpp_expect(hex2str(spars.tp.da.tP_DA_NoPad.tP_DAValue));
1844
1845 rp_mo := ts_RP_SMMA_MO(spars.rp.msg_ref);
1846 l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_ORIG, ts_CP_DATA_MO(rp_mo));
1847 BSSAP.send(ts_PDU_DTAP_MO(l3_mo, spars.dlci, true));
1848 /* receive CP-ACK for CP-DATA above */
1849 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_SMS(spars.tid, c_TIF_REPL, tr_CP_ACK_MT), spars.dlci));
1850
1851 deactivate(d);
1852 setverdict(pass);
1853}
1854
Vadim Yanitskiyd4cc1502018-11-12 01:46:21 +07001855/* Submit a MO-SMS over an already existing DTAP connection */
1856function f_mo_sms_submit(inout SmsParameters spars)
Harald Weltef45efeb2018-04-09 18:19:24 +02001857runs on BSC_ConnHdlr {
1858 var template (value) TPDU_RP_DATA_MS_SGSN tp_mo;
1859 var template (value) RPDU_MS_SGSN rp_mo;
1860 var template (value) PDU_ML3_MS_NW l3_mo;
1861
Harald Weltef45efeb2018-04-09 18:19:24 +02001862 var default d := activate(as_other_sms());
1863
Harald Weltef640a012018-04-14 17:49:21 +02001864 /* just in case this is routed to SMPP.. */
1865 f_create_smpp_expect(hex2str(spars.tp.da.tP_DA_NoPad.tP_DAValue));
1866
Harald Weltef45efeb2018-04-09 18:19:24 +02001867 tp_mo := ts_SMS_SUBMIT(spars.tp.msg_ref, spars.tp.da, spars.tp.pid, spars.tp.dcs,
1868 spars.tp.udl, spars.tp.ud);
Vadim Yanitskiy437b5a62019-12-15 14:13:39 +09001869 rp_mo := ts_RP_DATA_MO(spars.rp.msg_ref, omit, spars.rp.smsc_addr, tp_mo);
Harald Weltef45efeb2018-04-09 18:19:24 +02001870 l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_ORIG, ts_CP_DATA_MO(rp_mo));
1871 BSSAP.send(ts_PDU_DTAP_MO(l3_mo, spars.dlci, true));
1872 /* receive CP-ACK for CP-DATA above */
1873 BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_SMS(spars.tid, c_TIF_REPL, tr_CP_ACK_MT), spars.dlci));
1874
Vadim Yanitskiyd4cc1502018-11-12 01:46:21 +07001875 deactivate(d);
1876 setverdict(pass);
1877}
1878
1879/* Wait RP-ACK for MO-SMS on an already existing DTAP connection */
1880function f_mo_sms_wait_rp_ack(inout SmsParameters spars)
1881runs on BSC_ConnHdlr {
1882 var template (value) PDU_ML3_MS_NW l3_mo;
1883
1884 var template TPDU_RP_DATA_SGSN_MS tp_mt;
1885 var template RPDU_SGSN_MS rp_mt;
1886 var template PDU_ML3_NW_MS l3_mt;
1887
1888 var default d := activate(as_other_sms());
1889
1890 /* just in case this is routed to SMPP.. */
1891 f_create_smpp_expect(hex2str(spars.tp.da.tP_DA_NoPad.tP_DAValue));
1892
Harald Weltef45efeb2018-04-09 18:19:24 +02001893 if (ispresent(spars.exp_rp_err)) {
1894 /* expect an RP-ERROR message from MSC with given cause */
1895 rp_mt := tr_RP_ERROR_MT(spars.rp.msg_ref, spars.exp_rp_err);
1896 l3_mt := tr_ML3_MT_SMS(spars.tid, c_TIF_REPL, tr_CP_DATA_MT(rp_mt));
1897 BSSAP.receive(tr_PDU_DTAP_MT(l3_mt, spars.dlci));
1898 /* send CP-ACK for CP-DATA just received */
1899 l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_ORIG, ts_CP_ACK_MO);
1900 BSSAP.send(ts_PDU_DTAP_MO(l3_mo, spars.dlci, true));
1901 } else {
1902 /* expect RP-ACK for RP-DATA */
1903 rp_mt := tr_RP_ACK_MT(spars.rp.msg_ref);
1904 l3_mt := tr_ML3_MT_SMS(spars.tid, c_TIF_REPL, tr_CP_DATA_MT(rp_mt));
1905 BSSAP.receive(tr_PDU_DTAP_MT(l3_mt, spars.dlci));
1906 /* send CP-ACO for CP-DATA just received */
1907 l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_ORIG, ts_CP_ACK_MO);
1908 BSSAP.send(ts_PDU_DTAP_MO(l3_mo, spars.dlci, true));
1909 }
Vadim Yanitskiyd4cc1502018-11-12 01:46:21 +07001910
Harald Weltef45efeb2018-04-09 18:19:24 +02001911 deactivate(d);
1912 setverdict(pass);
1913}
1914
Vadim Yanitskiyd4cc1502018-11-12 01:46:21 +07001915/* Submit a MO-SMS, and wait for RP-ACK on an existing
1916 * (and authenticated, ...) DTAP connection */
1917function f_mo_sms(inout SmsParameters spars)
1918runs on BSC_ConnHdlr {
1919 f_mo_sms_submit(spars);
1920 f_mo_sms_wait_rp_ack(spars);
1921}
1922
Neels Hofmeyrb58d9742019-11-27 18:44:54 +01001923function f_mt_sms_deliver_pdu(in SmsParameters spars)
1924runs on BSC_ConnHdlr
1925return template PDU_DTAP_MT {
1926 var template TPDU_RP_DATA_SGSN_MS tp_mt := tr_SMS_DELIVER(?, spars.tp.ud, spars.tp.pid, spars.tp.dcs, ?);
1927 var template RPDU_SGSN_MS rp_mt := tr_RP_DATA_MT(?, spars.rp.smsc_addr, omit, tp_mt);
1928 var template PDU_ML3_NW_MS l3_mt := tr_ML3_MT_SMS(?, c_TIF_ORIG, tr_CP_DATA_MT(rp_mt));
1929 return tr_PDU_DTAP_MT(l3_mt, spars.dlci);
1930}
1931
Vadim Yanitskiy2159acb2018-11-24 02:50:29 +07001932/* Wait for MT SMS on an already existing DTAP connection */
1933function f_mt_sms_expect(inout SmsParameters spars)
Harald Weltef45efeb2018-04-09 18:19:24 +02001934runs on BSC_ConnHdlr {
Harald Weltef45efeb2018-04-09 18:19:24 +02001935 var template (value) PDU_ML3_MS_NW l3_mo;
Harald Weltef45efeb2018-04-09 18:19:24 +02001936 var PDU_DTAP_MT dtap_mt;
1937
1938 var default d := activate(as_other_sms());
1939
1940 /* Expect CP-DATA(RP-DATA(SMS-DELIVER)) */
Neels Hofmeyrb58d9742019-11-27 18:44:54 +01001941 BSSAP.receive(f_mt_sms_deliver_pdu(spars)) -> value dtap_mt;
Vadim Yanitskiy2159acb2018-11-24 02:50:29 +07001942
Harald Weltef45efeb2018-04-09 18:19:24 +02001943 /* Extract relevant identifiers */
1944 spars.tid := bit2int(dtap_mt.dtap.tiOrSkip.transactionId.tio);
1945 spars.rp.msg_ref := dtap_mt.dtap.msgs.sms.cP_DATA.cP_User_Data.cP_RPDU.rP_DATA_SGSN_MS.rP_MessageReference;
1946
1947 /* send CP-ACK for CP-DATA just received */
1948 l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_REPL, ts_CP_ACK_MO);
1949 BSSAP.send(ts_PDU_DTAP_MO(l3_mo, spars.dlci, true));
1950
Vadim Yanitskiy2159acb2018-11-24 02:50:29 +07001951 deactivate(d);
1952 setverdict(pass);
1953}
1954
1955/* Send RP-ACK for MT-SMS over an already existing DTAP connection */
1956function f_mt_sms_send_rp_ack(inout SmsParameters spars)
1957runs on BSC_ConnHdlr {
1958 var template (value) RPDU_MS_SGSN rp_mo;
1959 var template (value) PDU_ML3_MS_NW l3_mo;
1960 var template PDU_ML3_NW_MS l3_mt;
1961
1962 var default d := activate(as_other_sms());
1963
Harald Weltef45efeb2018-04-09 18:19:24 +02001964 /* send RP-ACK for RP-DATA */
1965 rp_mo := ts_RP_ACK_MO(spars.rp.msg_ref);
1966 l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_REPL, ts_CP_DATA_MO(rp_mo));
1967 BSSAP.send(ts_PDU_DTAP_MO(l3_mo, spars.dlci, true));
1968
1969 /* expect CP-ACK for CP-DATA(RP-ACK) just sent */
Harald Weltee008eea2018-04-14 21:05:48 +02001970 l3_mt := tr_ML3_MT_SMS(spars.tid, c_TIF_ORIG, tr_CP_ACK_MT);
Harald Weltef45efeb2018-04-09 18:19:24 +02001971 BSSAP.receive(tr_PDU_DTAP_MT(l3_mt, spars.dlci));
1972
1973 deactivate(d);
1974 setverdict(pass);
1975}
1976
Vadim Yanitskiy56318522018-11-24 03:40:47 +07001977/* Send RP-ERROR for MT-SMS over an already existing DTAP connection */
1978function f_mt_sms_send_rp_error(inout SmsParameters spars, uint7_t cause)
1979runs on BSC_ConnHdlr {
1980 var template (value) RPDU_MS_SGSN rp_mo;
1981 var template (value) PDU_ML3_MS_NW l3_mo;
1982 var template PDU_ML3_NW_MS l3_mt;
1983
1984 var default d := activate(as_other_sms());
1985
1986 /* send RP-ACK for RP-DATA */
1987 rp_mo := ts_RP_ERROR_MO(spars.rp.msg_ref, cause);
1988 l3_mo := ts_ML3_MO_SMS(spars.tid, c_TIF_REPL, ts_CP_DATA_MO(rp_mo));
1989 BSSAP.send(ts_PDU_DTAP_MO(l3_mo, spars.dlci, true));
1990
1991 /* expect CP-ACK for CP-DATA(RP-ERROR) just sent */
1992 l3_mt := tr_ML3_MT_SMS(spars.tid, c_TIF_ORIG, tr_CP_ACK_MT);
1993 BSSAP.receive(tr_PDU_DTAP_MT(l3_mt, spars.dlci));
1994
1995 deactivate(d);
1996 setverdict(pass);
1997}
1998
Vadim Yanitskiy2159acb2018-11-24 02:50:29 +07001999/* Wait for a MT-SMS and send RP-ACK over an already existing
2000 * (and authenticated, ...) DTAP connection */
2001function f_mt_sms(inout SmsParameters spars)
2002runs on BSC_ConnHdlr {
2003 f_mt_sms_expect(spars);
2004 f_mt_sms_send_rp_ack(spars);
2005}
Harald Weltea49e36e2018-01-21 19:29:33 +01002006
2007
Harald Weltea49e36e2018-01-21 19:29:33 +01002008}