blob: 9f8f8f8f17ef60405fdda8330a93936b575d06b1 [file] [log] [blame]
Harald Welteafec4712018-03-19 22:52:17 +01001module SIP_Tests {
2
Harald Welte34b5a952019-05-27 11:54:11 +02003/* osmo-sip-connector test suite in TTCN-3
4 * (C) 2018-2019 Harald Welte <laforge@gnumonks.org>
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 Welteafec4712018-03-19 22:52:17 +010013import from General_Types all;
14import from Osmocom_Types all;
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +020015import from Native_Functions all;
16import from Misc_Helpers all;
Harald Welteafec4712018-03-19 22:52:17 +010017
18import from Osmocom_CTRL_Functions all;
19import from Osmocom_CTRL_Types all;
20import from Osmocom_CTRL_Adapter all;
21
22import from TELNETasp_PortType all;
23import from Osmocom_VTY_Functions all;
24
25import from MNCC_Emulation all;
26import from MNCC_Types all;
27
28import from SDP_Types all;
29
30import from SIP_Emulation all;
31import from SIPmsg_Types all;
Harald Welteb0d93602018-03-20 18:09:34 +010032import from SIP_Templates all;
Harald Welteafec4712018-03-19 22:52:17 +010033
34modulepar {
Harald Welteb0d93602018-03-20 18:09:34 +010035 charstring mp_local_host := "127.0.0.2";
Harald Welteafec4712018-03-19 22:52:17 +010036 charstring mp_osmosip_host := "127.0.0.1";
37 integer mp_osmosip_port_ctrl := -1; /* RFU */
38 charstring mp_mncc := "/tmp/mncc";
39}
40
41type component test_CT extends CTRL_Adapter_CT {
42 var MNCC_Emulation_CT vc_MNCC;
43 var SIP_Emulation_CT vc_SIP;
44
45 port TELNETasp_PT SIPVTY;
46}
47
48type component ConnHdlr extends SIP_ConnHdlr, MNCC_ConnHdlr {
49 var ConnHdlrPars g_pars;
50 timer g_Tguard;
51}
52
53type record ConnHdlrPars {
Harald Welteb0d93602018-03-20 18:09:34 +010054 float t_guard,
55 CallPars g_cp optional
56}
57
58type record CallPars {
59 boolean is_mo,
60 charstring calling,
61 charstring called,
62
63 uint32_t mncc_call_id optional,
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +020064 CallParsComputed comp optional,
Harald Welteb0d93602018-03-20 18:09:34 +010065
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +020066 charstring sip_rtp_addr,
67 uint16_t sip_rtp_port,
68 charstring cn_rtp_addr,
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +020069 uint16_t cn_rtp_port,
70
71 /* Send SDP to MNCC, and expect to receive SDP from MNCC. mncc_with_sdp := false tests legacy compatibility to
72 * the time when we did not include SDP in MNCC messages. mncc_with_sdp := true expects SDP to pass through the
73 * SUT osmo-sip-connector unchanged. */
74 boolean mncc_with_sdp
Harald Welteb0d93602018-03-20 18:09:34 +010075}
76
77type record CallParsComputed {
78 CallidString sip_call_id,
79 SipAddr sip_url_ext,
80 SipAddr sip_url_gsm,
81 charstring sip_body,
82 integer sip_seq_nr
83}
84
Neels Hofmeyra0d015b2023-09-13 04:32:14 +020085private template (value) CallPars t_CallPars(boolean is_mo, boolean mncc_with_sdp := true) := {
Harald Welteb0d93602018-03-20 18:09:34 +010086 is_mo := is_mo,
87 calling := "12345",
88 called := "98766",
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +020089
Harald Welteb0d93602018-03-20 18:09:34 +010090 mncc_call_id := omit,
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +020091 comp := omit,
92 sip_rtp_addr := "1.2.3.4",
93 sip_rtp_port := 1234,
94 cn_rtp_addr := "5.6.7.8",
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +020095 cn_rtp_port := 5678,
96 mncc_with_sdp := mncc_with_sdp
Harald Welteb0d93602018-03-20 18:09:34 +010097}
98
99private function f_CallPars_compute(inout CallPars cp) {
100 if (cp.is_mo) {
101 cp.comp.sip_url_ext := valueof(ts_SipAddr(cp.called, mp_local_host, 5060));
102 cp.comp.sip_url_gsm := valueof(ts_SipAddr(cp.calling, mp_osmosip_host, 5060));
103 cp.mncc_call_id := f_rnd_int(429496725);
104 } else {
105 cp.comp.sip_url_ext := valueof(ts_SipAddr(cp.calling, mp_local_host, 5060));
106 cp.comp.sip_url_gsm := valueof(ts_SipAddr(cp.called, mp_osmosip_host, 5060));
107 cp.comp.sip_call_id := hex2str(f_rnd_hexstring(15));
108 }
109 cp.comp.sip_seq_nr := f_rnd_int(4294967295);
110 cp.comp.sip_body := "";
Harald Welteafec4712018-03-19 22:52:17 +0100111}
112
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200113private function f_mgcp_addr2addrtype(charstring addr) return charstring {
114 for (var integer i := 0; i < lengthof(addr); i := i + 1) {
115 if (addr[i] == ":") {
116 return "IP6";
117 }
118 }
119 return "IP4";
120}
Harald Welteafec4712018-03-19 22:52:17 +0100121function f_init_mncc(charstring id) runs on test_CT {
122 id := id & "-MNCC";
123 var MnccOps ops := {
124 create_cb := refers(MNCC_Emulation.ExpectedCreateCallback),
125 unitdata_cb := refers(MNCC_Emulation.DummyUnitdataCallback)
126 };
127
128 vc_MNCC := MNCC_Emulation_CT.create(id);
129 map(vc_MNCC:MNCC, system:MNCC_CODEC_PT);
130 vc_MNCC.start(MNCC_Emulation.main(ops, id, mp_mncc, true));
131}
132
133function f_init() runs on test_CT {
Pau Espin Pedrol9a5b8ff2021-01-04 19:01:31 +0100134 //f_ipa_ctrl_start_client(mp_osmosip_host, mp_osmosip_port_ctrl);
Harald Welteafec4712018-03-19 22:52:17 +0100135 f_init_mncc("SIP_Test");
136 log("end of f_init_mncc");
137 f_init_sip(vc_SIP, "SIP_Test");
138 log("end of f_init_sip");
139
140 map(self:SIPVTY, system:SIPVTY);
141 f_vty_set_prompts(SIPVTY);
142 f_vty_transceive(SIPVTY, "enable");
143 log("end of f_init");
144}
145
146type function void_fn(charstring id) runs on ConnHdlr;
147
148function f_start_handler(void_fn fn, ConnHdlrPars pars)
149runs on test_CT return ConnHdlr {
150 var ConnHdlr vc_conn;
151 var charstring id := testcasename();
152
153 vc_conn := ConnHdlr.create(id);
154
155 connect(vc_conn:SIP, vc_SIP:CLIENT);
156 connect(vc_conn:SIP_PROC, vc_SIP:CLIENT_PROC);
157
158 connect(vc_conn:MNCC, vc_MNCC:MNCC_CLIENT);
159 connect(vc_conn:MNCC_PROC, vc_MNCC:MNCC_PROC);
160
161 vc_conn.start(f_handler_init(fn, id, pars));
162 return vc_conn;
163}
164
165private altstep as_Tguard() runs on ConnHdlr {
166 [] g_Tguard.timeout {
167 setverdict(fail, "Tguard timeout");
Daniel Willmannafce8662018-07-06 23:11:32 +0200168 mtc.stop;
Harald Welteafec4712018-03-19 22:52:17 +0100169 }
170}
171
172private function f_handler_init(void_fn fn, charstring id, ConnHdlrPars pars)
173runs on ConnHdlr {
174 g_pars := pars;
175 g_Tguard.start(pars.t_guard);
176 activate(as_Tguard());
177
178 /* call the user-supied test case function */
179 fn.apply(id);
180}
181
182
183template (value) ConnHdlrPars t_Pars := {
Harald Welteb0d93602018-03-20 18:09:34 +0100184 t_guard := 30.0,
185 g_cp := omit
Harald Welteafec4712018-03-19 22:52:17 +0100186}
187
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200188altstep as_SIP_expect_resp(template PDU_SIP_Response sip_expect) runs on ConnHdlr
189{
190 [] SIP.receive(sip_expect);
191 [] SIP.receive {
192 log("FAIL: expected SIP message ", sip_expect);
193 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Received unexpected SIP message");
194 }
195}
196
197function f_SIP_expect_req(template PDU_SIP_Request sip_expect) runs on ConnHdlr return PDU_SIP_Request
198{
199 var PDU_SIP_Request rx;
200 alt {
201 [] SIP.receive(sip_expect) -> value rx;
202 [] SIP.receive {
203 log("FAIL: expected SIP message ", sip_expect);
204 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Received unexpected SIP message");
205 }
206 }
207 return rx;
208}
209
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200210/* Update 'last_sdp', and match with expectation of what the current SDP should be.
211 * Useful to ensure that MNCC or SIP send and possibly resend only the expected SDP.
212 * last_sdp keeps the last non-empty rx_sdp, across multiple check_sdp() invocations.
213 * rx_sdp is the SDP charstring just received. If it is nonempty, update last_sdp to rx_sdp.
214 * After updating last_sdp as appropriate, match last_sdp with expect_sdp. */
215private function check_sdp(inout charstring last_sdp,
216 charstring rx_sdp,
217 template charstring expect_sdp)
218{
219 /* If there is new SDP, store it. */
220 if (lengthof(rx_sdp) > 0) {
221 if (last_sdp != rx_sdp) {
222 log("SDP update from ", last_sdp, " to ", rx_sdp);
223 }
224
225 /* If MNCC sent SDP data, remember it as the last valid SDP */
226 last_sdp := rx_sdp;
227 }
228 /* Validate expectations of the SDP data */
229 if (not match(last_sdp, expect_sdp)) {
230 log("FAIL: expected SDP ", expect_sdp, " but got ", last_sdp);
231 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "unexpected SDP");
232 }
233}
234
Harald Welteb0d93602018-03-20 18:09:34 +0100235/* Establish a mobile terminated call described in 'cp' */
236function f_establish_mt(inout CallPars cp) runs on ConnHdlr {
237 var template SipAddr sip_addr_gsm := tr_SipAddr_from_val(cp.comp.sip_url_gsm);
238 var template SipAddr sip_addr_ext := tr_SipAddr_from_val(cp.comp.sip_url_ext);
Harald Welteb0d93602018-03-20 18:09:34 +0100239 var MNCC_PDU mncc;
Harald Welteafec4712018-03-19 22:52:17 +0100240
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200241 /* The last SDP that the MSC received via MNCC from osmo-sip-connector */
242 var charstring sdp_to_msc := "";
243 /* At first, allow any empty and nonempty SDP. As the test progresses, this may expect specific SDP instead. */
244 var template charstring expect_sdp_to_msc := *;
245
246 /* If cp.mncc_with_sdp == true, expect SDP forwarding like this:
247 *
248 * SDP1: SIP agent's RTP and codec info
249 * SDP2: osmo-msc's RTP and codec info
250 *
251 * MNCC osmo-sip-connector SIP
252 * |<--SDP1----- SIP Invite
253 * |-----------> SIP (Invite) Trying
254 * <--SDP1-------| MNCC SETUP req
255 * ------------->| MNCC CALL CONF ind
256 * <-------------| MNCC RTP CREATE (SDP optional, still unchanged from SDP1)
257 * -------SDP2-->| MNCC RTP CREATE
258 * ------------->| MNCC ALERT ind
259 * |--------------> SIP (Invite) Ringing
260 * (MT picks up) |
261 * ------------->| MNCC SETUP CNF
262 * <-------------| MNCC RTP CONNECT (SDP optional, still unchanged from SDP1)
263 * |--------SDP2--> SIP (Invite) OK
264 * |<-------------- SIP ACK
265 * <-------------| MNCC SETUP COMPL (SDP optional, still unchanged from SDP1)
266 */
267
Harald Welteb0d93602018-03-20 18:09:34 +0100268 /* Ask MNCC_Emulation to "expect" a call to the given called number */
269 f_create_mncc_expect(cp.called);
Harald Welteafec4712018-03-19 22:52:17 +0100270
Harald Welteb0d93602018-03-20 18:09:34 +0100271 /* OSC <- SIP: A party sends SIP invite for a MT-call into OSC */
272 SIP.send(ts_SIP_INVITE(cp.comp.sip_call_id, cp.comp.sip_url_ext, cp.comp.sip_url_gsm,
273 cp.comp.sip_seq_nr, cp.comp.sip_body));
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200274 if (cp.mncc_with_sdp) {
275 /* We just sent SDP via SIP, now expect the same SDP in MNCC to the MSC */
276 expect_sdp_to_msc := cp.comp.sip_body;
277 }
278
Harald Welteb0d93602018-03-20 18:09:34 +0100279 /* OSC -> SIP */
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200280 as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, *,
281 "INVITE", 100, ?, "Trying", *));
Harald Welteafec4712018-03-19 22:52:17 +0100282
Pau Espin Pedrol37cf4082020-09-23 16:59:16 +0200283 alt {
284 /* MSC <- OSC: OSC generates MNCC_SETUP_REQ from INVITE */
285 [] MNCC.receive(tr_MNCC_SETUP_req) -> value mncc {
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200286 cp.mncc_call_id := mncc.u.signal.callref;
287 /* Expect the SDP sent via SIP to arrive in MNCC */
288 check_sdp(sdp_to_msc, mncc.u.signal.sdp, expect_sdp_to_msc);
Pau Espin Pedrol37cf4082020-09-23 16:59:16 +0200289 }
290 [] SIP.receive {
Neels Hofmeyrbaf02722023-09-12 02:10:11 +0200291 setverdict(fail, "Received unexpected SIP response");
Pau Espin Pedrol37cf4082020-09-23 16:59:16 +0200292 SIP.send(ts_SIP_ACK(cp.comp.sip_call_id, cp.comp.sip_url_ext, cp.comp.sip_url_gsm,
293 cp.comp.sip_seq_nr, omit));
294 mtc.stop;
295 }
296 }
297
Harald Welteb0d93602018-03-20 18:09:34 +0100298 /* MSC -> OSC: After MS sends CALL CONF in response to SETUP */
299 MNCC.send(ts_MNCC_CALL_CONF_ind(cp.mncc_call_id));
300 /* MSC <- OSC: OSC asks MSC to create RTP socket */
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200301 MNCC.receive(tr_MNCC_RTP_CREATE(cp.mncc_call_id)) -> value mncc {
302 check_sdp(sdp_to_msc, mncc.u.rtp.sdp, expect_sdp_to_msc);
303 }
304
305 /* MSC -> OSC: SDP that the MSC will send via MNCC */
306 var charstring cn_sdp := "v=0\r\no=Osmocom 0 0 IN IP4 1.1.1.1\r\ns=GSM Call\r\nc=IN " &
307 f_mgcp_addr2addrtype(cp.cn_rtp_addr) & " " & cp.cn_rtp_addr &
308 "\r\nt=0 0\r\nm=audio " & int2str(cp.cn_rtp_port) &
309 " RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
310 /* OSC -> SIP: what SDP to expect in SIP from osmo-sip-connector */
311 var template charstring expect_sdp_to_sip := pattern "*" & cp.cn_rtp_addr & "*";
312
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200313 mncc := valueof(ts_MNCC_RTP_CREATE(cp.mncc_call_id));
314 mncc.u.rtp.is_ipv6 := f_addr_is_ipv6(cp.cn_rtp_addr);
315 mncc.u.rtp.ip := f_addrstr2addr(cp.cn_rtp_addr);
316 mncc.u.rtp.rtp_port := cp.cn_rtp_port;
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200317 if (cp.mncc_with_sdp) {
318 /* MSC -> OSC: tell OSC our RTP info in SDP form */
319 mncc.u.rtp.sdp := cn_sdp;
320 /* OSC -> SIP: and expect it unchanged on SIP later, but allow osmo-sip-connector to append an
321 * "a=sendrecv;" */
322 expect_sdp_to_sip := pattern cn_sdp & "*";
323 }
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200324 MNCC.send(mncc);
Harald Welteafec4712018-03-19 22:52:17 +0100325
Harald Welteb0d93602018-03-20 18:09:34 +0100326 /* MSC -> OSC: After MS is ringing and sent CC ALERTING */
327 MNCC.send(ts_MNCC_ALERT_ind(cp.mncc_call_id));
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200328
329 /* Now expect SIP response "Ringing" back to MO, containing the same SDP information as in the MNCC RTP CREATE
330 * sent to OSC above */
Harald Welteb0d93602018-03-20 18:09:34 +0100331 SIP.clear;
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200332
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200333 /* 180 Ringing should not contain any SDP. */
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200334 as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, *,
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200335 "INVITE", 180, ?, "Ringing", omit));
Harald Welteafec4712018-03-19 22:52:17 +0100336
Harald Welteb0d93602018-03-20 18:09:34 +0100337 /* MSC -> OSC: After MT user has picked up and sent CC CONNECT */
338 MNCC.send(ts_MNCC_SETUP_CNF(cp.mncc_call_id));
339
340 SIP.clear;
Harald Welteb0d93602018-03-20 18:09:34 +0100341 /* MSC <- OSC: OSC asks MSC to connect its RTP stream to remote end */
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200342 MNCC.receive(tr_MNCC_RTP_CONNECT(cp.mncc_call_id, f_addrstr2addr(cp.sip_rtp_addr), cp.sip_rtp_port))
343 -> value mncc {
344 check_sdp(sdp_to_msc, mncc.u.rtp.sdp, expect_sdp_to_msc);
345 }
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200346
Harald Welteb0d93602018-03-20 18:09:34 +0100347 /* OSC -> SIP: OSC confirms call establishment to SIP side */
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200348 as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, contact_addr := ?,
349 method := "INVITE", status_code := 200,
350 seq_nr := ?, reason := "OK",
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200351 body := expect_sdp_to_sip));
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200352
Harald Welteb0d93602018-03-20 18:09:34 +0100353 /* OSC <- SIP: SIP world acknowledges "200 OK" */
354 SIP.send(ts_SIP_ACK(cp.comp.sip_call_id, cp.comp.sip_url_ext, cp.comp.sip_url_gsm,
355 cp.comp.sip_seq_nr, omit));
356 /* MSC <- OSC: OSC sends SETUP COMPL to MNCC (which triggers CC CONNECT ACK */
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200357 MNCC.receive(tr_MNCC_SETUP_COMPL_req(cp.mncc_call_id)) -> value mncc {
358 check_sdp(sdp_to_msc, mncc.u.signal.sdp, expect_sdp_to_msc);
359 }
Harald Welteafec4712018-03-19 22:52:17 +0100360}
361
Harald Welteb0d93602018-03-20 18:09:34 +0100362/* Establish a mobile originated call described in 'cp' */
363function f_establish_mo(inout CallPars cp) runs on ConnHdlr {
364 var MNCC_number dst := valueof(ts_MNCC_number(cp.called, GSM48_TON_UNKNOWN));
365 var MNCC_number src := valueof(ts_MNCC_number(cp.calling, GSM48_TON_UNKNOWN));
366 var template SipAddr sip_addr_gsm := tr_SipAddr_from_val(cp.comp.sip_url_gsm);
367 var template SipAddr sip_addr_ext := tr_SipAddr_from_val(cp.comp.sip_url_ext);
368 var PDU_SIP_Request sip_req;
369 var integer seq_nr;
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200370 var MNCC_PDU mncc;
371
372 /* The last SDP that the MSC received via MNCC from osmo-sip-connector */
373 var charstring sdp_to_msc := "";
374 /* At first, allow any empty and nonempty SDP. As the test progresses, this may expect specific SDP instead. */
375 var template charstring expect_sdp_to_msc := *;
376
377 /* If cp.mncc_with_sdp == true, expect SDP forwarding like this:
378 *
379 * SDP1: osmo-msc's RTP and codec info
380 * SDP2: SIP agent's RTP and codec info
381 *
382 * MNCC osmo-sip-connector SIP
383 * -------SDP1-->| MNCC SETUP ind
384 * <-------------| MNCC RTP CREATE (?)
385 * |-----SDP1--> SIP Invite
386 * |<----------- SIP (Invite) Trying
387 * <-------------| MNCC CALL PROC req
388 * |<----------- SIP (Invite) Ringing
389 * <-------------| MNCC ALERT req
390 * | (MT picks up)
391 * |<--SDP2----- SIP (Invite) OK
392 * <--SDP2-------| MNCC RTP CONNECT (SDP optional, still unchanged from SDP2)
393 * <-------------| MNCC SETUP rsp (SDP optional, still unchanged from SDP2)
394 * ------------->| MNCC SETUP COMPL ind (SDP optional, still unchanged from SDP1)
395 * |------------> SIP ACK
396 */
397
398 var charstring cn_sdp := "v=0\r\no=Osmocom 0 0 IN IP4 1.1.1.1\r\ns=GSM Call\r\nc=IN " &
399 f_mgcp_addr2addrtype(cp.cn_rtp_addr) & " " & cp.cn_rtp_addr &
400 "\r\nt=0 0\r\nm=audio " & int2str(cp.cn_rtp_port) &
401 " RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
Harald Welteb0d93602018-03-20 18:09:34 +0100402
403 f_create_sip_expect(cp.comp.sip_url_ext.addr.nameAddr.addrSpec);
404
405 /* MSC -> OSC: MSC sends SETUP.ind after CC SETUP was received from MS */
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200406 mncc := valueof(ts_MNCC_SETUP_ind(cp.mncc_call_id, dst, src, "262420123456789"));
407 if (cp.mncc_with_sdp) {
408 mncc.u.signal.sdp := cn_sdp;
409 }
410 MNCC.send(mncc);
411
Harald Welteb0d93602018-03-20 18:09:34 +0100412 /* MSC <- OSC: Create GSM side RTP socket */
413 MNCC.receive(tr_MNCC_RTP_CREATE(cp.mncc_call_id)) {
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200414 mncc := valueof(ts_MNCC_RTP_CREATE(cp.mncc_call_id));
Harald Welteb0d93602018-03-20 18:09:34 +0100415 mncc.u.rtp.payload_msg_type := oct2int('0300'O);
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200416 /* FIXME: makes no sense to send cp.cn_rtp_addr back to the cn. */
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200417 mncc.u.rtp.is_ipv6 := f_addr_is_ipv6(cp.cn_rtp_addr);
418 mncc.u.rtp.ip := f_addrstr2addr(cp.cn_rtp_addr);
419 mncc.u.rtp.rtp_port := cp.cn_rtp_port;
420 MNCC.send(mncc);
Harald Welteb0d93602018-03-20 18:09:34 +0100421 }
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200422
Harald Welteb0d93602018-03-20 18:09:34 +0100423 /* OSC -> SIP: Send INVITE with GSM side IP/Port in SDP */
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200424 var template charstring expect_sdp_to_sip := ?;
425 if (cp.mncc_with_sdp) {
426 /* Expect the same SDP as sent to osmo-sip-connector in MNCC, and allow osmo-sip-connector to append an
427 * "a=sendrecv;" */
428 expect_sdp_to_sip := pattern cn_sdp & "*";
429 }
430 sip_req := f_SIP_expect_req(tr_SIP_INVITE(?, sip_addr_gsm, sip_addr_ext, ?, expect_sdp_to_sip));
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200431 cp.comp.sip_url_gsm.params := sip_req.msgHeader.fromField.fromParams;
432 cp.comp.sip_call_id := sip_req.msgHeader.callId.callid;
433 seq_nr := sip_req.msgHeader.cSeq.seqNumber;
434
Harald Welteb0d93602018-03-20 18:09:34 +0100435 /* OSC <- SIP: Notify call is proceeding */
436 SIP.send(ts_SIP_Response(cp.comp.sip_call_id, cp.comp.sip_url_gsm, cp.comp.sip_url_ext,
437 "INVITE", 100, seq_nr, "Trying", sip_req.msgHeader.via));
438 /* MSC <- OSC: "100 Trying" translated to MNCC_CALL_PROC_REQ */
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200439 MNCC.receive(tr_MNCC_CALL_PROC_req(cp.mncc_call_id)) -> value mncc {
440 check_sdp(sdp_to_msc, mncc.u.signal.sdp, "");
441 }
Harald Welteb0d93602018-03-20 18:09:34 +0100442
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200443 /* OSC <- SIP: SIP-terminated user is ringing now. 180 Ringing should not contain any SDP. */
Harald Welteb0d93602018-03-20 18:09:34 +0100444 SIP.send(ts_SIP_Response(cp.comp.sip_call_id, cp.comp.sip_url_gsm, cp.comp.sip_url_ext,
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200445 "INVITE", 180, seq_nr, "Ringing", sip_req.msgHeader.via, omit));
Harald Welteb0d93602018-03-20 18:09:34 +0100446
447 /* MSC <- OSC: "180 Ringing" translated to MNCC_ALERT_REQ */
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200448 MNCC.receive(tr_MNCC_ALERT_req(cp.mncc_call_id)) -> value mncc {
449 check_sdp(sdp_to_msc, mncc.u.signal.sdp, expect_sdp_to_msc);
450 }
Harald Welteb0d93602018-03-20 18:09:34 +0100451
452 /* OSC <- SIP: SIP-terminated user has accepted the call */
453 SIP.send(ts_SIP_Response(cp.comp.sip_call_id, cp.comp.sip_url_gsm, cp.comp.sip_url_ext,
454 "INVITE", 200, seq_nr, "OK", sip_req.msgHeader.via,
455 cp.comp.sip_body));
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200456
457 if (cp.mncc_with_sdp) {
458 /* If we expect SDP forwarding, from now on expect MNCC to reflect the SDP that we just sent on SIP. */
459 expect_sdp_to_msc := cp.comp.sip_body;
460 }
461 /* If we don't expect SDP forwarding, just keep expect_sdp_to_msc := *. */
462
463 MNCC.receive(tr_MNCC_RTP_CONNECT(cp.mncc_call_id)) -> value mncc {
464 check_sdp(sdp_to_msc, mncc.u.rtp.sdp, expect_sdp_to_msc);
465 }
Harald Welteb0d93602018-03-20 18:09:34 +0100466 /* MSC <- OSC: "200 OK" translated to MNCC_SETUP_RSP */
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200467 MNCC.receive(tr_MNCC_SETUP_rsp(cp.mncc_call_id)) -> value mncc {
468 check_sdp(sdp_to_msc, mncc.u.signal.sdp, expect_sdp_to_msc);
469 }
Harald Welteb0d93602018-03-20 18:09:34 +0100470
471 /* MSC -> OSC: CC CONNECT ACK was received from MS */
472 MNCC.send(ts_MNCC_SETUP_COMPL_ind(cp.mncc_call_id));
473 /* OSC -> SIP: Acknowledge the call */
474 SIP.receive(tr_SIP_ACK(cp.comp.sip_call_id, sip_addr_gsm, sip_addr_ext, ?, omit));
475}
476
477/* Release call from the mobile side */
478function f_release_mobile(inout CallPars cp) runs on ConnHdlr {
479 var template SipAddr sip_addr_gsm := tr_SipAddr_from_val(cp.comp.sip_url_gsm);
480 var template SipAddr sip_addr_ext := tr_SipAddr_from_val(cp.comp.sip_url_ext);
481 var PDU_SIP_Request sip_req;
482 SIP.clear;
483 /* MSC -> OSC: Simulate a CC DISCONNET from the MT user */
484 MNCC.send(ts_MNCC_DISC_ind(cp.mncc_call_id, ts_MNCC_cause(0)));
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200485
Harald Welteb0d93602018-03-20 18:09:34 +0100486 /* OSC -> SIP: Expect BYE from OSC to SIP side */
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200487 sip_req := f_SIP_expect_req(tr_SIP_BYE(cp.comp.sip_call_id, sip_addr_gsm, sip_addr_ext, ?, *));
488 cp.comp.sip_url_gsm.params := sip_req.msgHeader.fromField.fromParams;
489
Harald Welteb0d93602018-03-20 18:09:34 +0100490 /* OSC <- SIP: Acknowledge the BYE */
491 SIP.send(ts_SIP_Response(cp.comp.sip_call_id, cp.comp.sip_url_gsm, cp.comp.sip_url_ext,
492 "BYE", 200, sip_req.msgHeader.cSeq.seqNumber, "OK",
493 sip_req.msgHeader.via));
494 /* MSC <- OSC: Send REL_REQ to MSC, triggers CC RELEASE REQ to MS */
495 MNCC.receive(tr_MNCC_REL_req(cp.mncc_call_id)); // CAUSE?
496 /* MSC -> OSC: MS has responded with CC CLEAR COMPL, triggers MNCC_REL_CNF */
497 MNCC.send(ts_MNCC_REL_cnf(cp.mncc_call_id, ts_MNCC_cause(0)));
498}
499
500/* Release call from the SIP side */
501function f_release_sip(inout CallPars cp) runs on ConnHdlr {
502 var template SipAddr sip_addr_gsm := tr_SipAddr_from_val(cp.comp.sip_url_gsm);
503 var template SipAddr sip_addr_ext := tr_SipAddr_from_val(cp.comp.sip_url_ext);
504 /* OSC <- SIP: SIP-side sends a BYE to OSC */
505 SIP.send(ts_SIP_BYE(cp.comp.sip_call_id, cp.comp.sip_url_ext, cp.comp.sip_url_gsm,
506 cp.comp.sip_seq_nr, omit));
507 /* MSC <- OSC: Expect OSC to cause MNCC Disconnect Request */
508 MNCC.receive(tr_MNCC_DISC_req(cp.mncc_call_id));
509 /* MSC -> OSC: Indicate GSM side release */
510 MNCC.send(ts_MNCC_REL_ind(cp.mncc_call_id, ts_MNCC_cause(0)));
511 /* OSC -> SIP: Confirmation to SIP side */
Neels Hofmeyrc35b6572023-09-12 02:16:43 +0200512 as_SIP_expect_resp(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, *,
513 "BYE", 200, cp.comp.sip_seq_nr, "OK", omit));
Harald Welteb0d93602018-03-20 18:09:34 +0100514}
515
516/* Successful MT Call, which is subsequently released by GSM side */
517private function f_TC_mt_success_rel_gsm(charstring id) runs on ConnHdlr {
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200518 var CallPars cp := g_pars.g_cp;
Harald Welteb0d93602018-03-20 18:09:34 +0100519 f_CallPars_compute(cp);
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200520 cp.comp.sip_body := "v=0\r\no=Osmocom 0 0 IN IP4 1.1.1.1\r\ns=GSM Call\r\nc=IN " &
521 f_mgcp_addr2addrtype(cp.sip_rtp_addr) & " " & cp.sip_rtp_addr &
522 "\r\nt=0 0\r\nm=audio " & int2str(cp.sip_rtp_port) &
523 " RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
Harald Welteb0d93602018-03-20 18:09:34 +0100524 f_sleep(3.0)
525
526 f_establish_mt(cp);
527 /* now call is fully established */
528 f_sleep(2.0);
529 f_release_mobile(cp);
530 setverdict(pass);
531}
532testcase TC_mt_success_rel_gsm() runs on test_CT {
Harald Welteafec4712018-03-19 22:52:17 +0100533 var ConnHdlrPars pars;
534 var ConnHdlr vc_conn;
Harald Welteafec4712018-03-19 22:52:17 +0100535 f_init();
Harald Welteafec4712018-03-19 22:52:17 +0100536 pars := valueof(t_Pars);
Neels Hofmeyra0d015b2023-09-13 04:32:14 +0200537 pars.g_cp := valueof(t_CallPars(false, false));
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200538 vc_conn := f_start_handler(refers(f_TC_mt_success_rel_gsm), pars);
539 vc_conn.done;
540}
541testcase TC_mt_success_rel_gsm_ipv6() runs on test_CT {
542 var ConnHdlrPars pars;
543 var ConnHdlr vc_conn;
544 f_init();
545 pars := valueof(t_Pars);
Neels Hofmeyra0d015b2023-09-13 04:32:14 +0200546 pars.g_cp := valueof(t_CallPars(false, false));
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200547 pars.g_cp.sip_rtp_addr := "::1";
548 pars.g_cp.cn_rtp_addr := "::2";
Harald Welteb0d93602018-03-20 18:09:34 +0100549 vc_conn := f_start_handler(refers(f_TC_mt_success_rel_gsm), pars);
550 vc_conn.done;
551}
552
553/* Successful MT Call, which is subsequently released by SIP side */
554private function f_TC_mt_success_rel_sip(charstring id) runs on ConnHdlr {
Neels Hofmeyrfb7f43b2023-09-13 05:42:41 +0200555 var CallPars cp := g_pars.g_cp;
Harald Welteb0d93602018-03-20 18:09:34 +0100556 f_CallPars_compute(cp);
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200557 cp.comp.sip_body := "v=0\r\no=Osmocom 0 0 IN IP4 1.1.1.1\r\ns=GSM Call\r\nc=IN " &
558 f_mgcp_addr2addrtype(cp.sip_rtp_addr) & " " & cp.sip_rtp_addr &
559 "\r\nt=0 0\r\nm=audio " & int2str(cp.sip_rtp_port) &
560 " RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
Harald Welteb0d93602018-03-20 18:09:34 +0100561 f_sleep(3.0)
562
563 f_establish_mt(cp);
564 /* now call is fully established */
565 f_sleep(2.0);
566 f_release_sip(cp);
567 setverdict(pass);
568}
569testcase TC_mt_success_rel_sip() runs on test_CT {
570 var ConnHdlrPars pars;
571 var ConnHdlr vc_conn;
572 f_init();
573 pars := valueof(t_Pars);
Neels Hofmeyra0d015b2023-09-13 04:32:14 +0200574 pars.g_cp := valueof(t_CallPars(false, false));
Harald Welteb0d93602018-03-20 18:09:34 +0100575 vc_conn := f_start_handler(refers(f_TC_mt_success_rel_sip), pars);
Harald Welteafec4712018-03-19 22:52:17 +0100576 vc_conn.done;
577}
578
579
Harald Welteb0d93602018-03-20 18:09:34 +0100580/* Successful MO Call, which is subsequently released by GSM side */
581private function f_TC_mo_success_rel_gsm(charstring id) runs on ConnHdlr {
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200582 var CallPars cp := g_pars.g_cp;
Harald Welteb0d93602018-03-20 18:09:34 +0100583 f_CallPars_compute(cp);
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200584 cp.comp.sip_body := "v=0\r\no=Osmocom 0 0 IN IP4 1.1.1.1\r\ns=GSM Call\r\nc=IN " &
585 f_mgcp_addr2addrtype(cp.sip_rtp_addr) & " " & cp.sip_rtp_addr &
586 "\r\nt=0 0\r\nm=audio " & int2str(cp.sip_rtp_port) &
587 " RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
Harald Welteb0d93602018-03-20 18:09:34 +0100588 f_sleep(3.0)
Harald Welteafec4712018-03-19 22:52:17 +0100589
Harald Welteb0d93602018-03-20 18:09:34 +0100590 f_establish_mo(cp);
591 /* now call is fully established */
592 f_sleep(2.0);
593 f_release_mobile(cp);
594 setverdict(pass);
595}
596testcase TC_mo_success_rel_gsm() runs on test_CT {
597 var ConnHdlrPars pars;
598 var ConnHdlr vc_conn;
599 f_init();
600 pars := valueof(t_Pars);
Neels Hofmeyra0d015b2023-09-13 04:32:14 +0200601 pars.g_cp := valueof(t_CallPars(true, false));
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200602 vc_conn := f_start_handler(refers(f_TC_mo_success_rel_gsm), pars);
603 vc_conn.done;
604}
605testcase TC_mo_success_rel_gsm_ipv6() runs on test_CT {
606 var ConnHdlrPars pars;
607 var ConnHdlr vc_conn;
608 f_init();
609 pars := valueof(t_Pars);
Neels Hofmeyra0d015b2023-09-13 04:32:14 +0200610 pars.g_cp := valueof(t_CallPars(true, false));
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200611 pars.g_cp.sip_rtp_addr := "::1";
612 pars.g_cp.cn_rtp_addr := "::2";
Harald Welteb0d93602018-03-20 18:09:34 +0100613 vc_conn := f_start_handler(refers(f_TC_mo_success_rel_gsm), pars);
614 vc_conn.done;
Harald Welteafec4712018-03-19 22:52:17 +0100615}
616
Harald Welteb0d93602018-03-20 18:09:34 +0100617/* Successful MO Call, which is subsequently released by SIP side */
618private function f_TC_mo_success_rel_sip(charstring id) runs on ConnHdlr {
Neels Hofmeyrfb7f43b2023-09-13 05:42:41 +0200619 var CallPars cp := g_pars.g_cp;
Harald Welteb0d93602018-03-20 18:09:34 +0100620 f_CallPars_compute(cp);
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200621 cp.comp.sip_body := "v=0\r\no=Osmocom 0 0 IN IP4 1.1.1.1\r\ns=GSM Call\r\nc=IN " &
622 f_mgcp_addr2addrtype(cp.sip_rtp_addr) & " " & cp.sip_rtp_addr &
623 "\r\nt=0 0\r\nm=audio " & int2str(cp.sip_rtp_port) &
624 " RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
Harald Welteb0d93602018-03-20 18:09:34 +0100625 f_sleep(3.0)
Harald Welteafec4712018-03-19 22:52:17 +0100626
Harald Welteb0d93602018-03-20 18:09:34 +0100627 f_establish_mo(cp);
628 /* now call is fully established */
629 f_sleep(2.0);
630 f_release_sip(cp);
631 setverdict(pass);
632}
633testcase TC_mo_success_rel_sip() runs on test_CT {
634 var ConnHdlrPars pars;
635 var ConnHdlr vc_conn;
636 f_init();
637 pars := valueof(t_Pars);
Neels Hofmeyra0d015b2023-09-13 04:32:14 +0200638 pars.g_cp := valueof(t_CallPars(is_mo := true, mncc_with_sdp := false));
Harald Welteb0d93602018-03-20 18:09:34 +0100639 vc_conn := f_start_handler(refers(f_TC_mo_success_rel_sip), pars);
640 vc_conn.done;
641}
Harald Welteafec4712018-03-19 22:52:17 +0100642
Harald Welteddf011e2019-04-19 09:39:35 +0200643/* SETUP followed by DISC results in lingering B-leg (OS#3518)*/
644private function f_TC_mo_setup_disc_late_rtp(charstring id) runs on ConnHdlr {
Neels Hofmeyrfb7f43b2023-09-13 05:42:41 +0200645 var CallPars cp := g_pars.g_cp;
Harald Welteddf011e2019-04-19 09:39:35 +0200646 f_CallPars_compute(cp);
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200647 cp.comp.sip_body := "v=0\r\no=Osmocom 0 0 IN IP4 1.1.1.1\r\ns=GSM Call\r\nc=IN " &
648 f_mgcp_addr2addrtype(cp.sip_rtp_addr) & " " & cp.sip_rtp_addr &
649 "\r\nt=0 0\r\nm=audio " & int2str(cp.sip_rtp_port) &
650 " RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
Harald Welteddf011e2019-04-19 09:39:35 +0200651 f_sleep(3.0);
652
653 var MNCC_number dst := valueof(ts_MNCC_number(cp.called, GSM48_TON_UNKNOWN));
654 var MNCC_number src := valueof(ts_MNCC_number(cp.calling, GSM48_TON_UNKNOWN));
655 var template SipAddr sip_addr_gsm := tr_SipAddr_from_val(cp.comp.sip_url_gsm);
656 var template SipAddr sip_addr_ext := tr_SipAddr_from_val(cp.comp.sip_url_ext);
657
658 f_create_sip_expect(cp.comp.sip_url_ext.addr.nameAddr.addrSpec);
659
660 /* MSC -> OSC: MSC sends SETUP.ind after CC SETUP was received from MS */
661 MNCC.send(ts_MNCC_SETUP_ind(cp.mncc_call_id, dst, src, "262420123456789"));
662
663 /* MSC -> OSC: Simulate a CC DISCONNET from the MT user *before* responding to the RTP_CREATE */
664 MNCC.send(ts_MNCC_DISC_ind(cp.mncc_call_id, ts_MNCC_cause(0)));
665
666 /* MSC <- OSC: Create GSM side RTP socket (too late) */
667 MNCC.receive(tr_MNCC_RTP_CREATE(cp.mncc_call_id)) {
668 var MNCC_PDU mncc := valueof(ts_MNCC_RTP_CREATE(cp.mncc_call_id));
669 mncc.u.rtp.payload_msg_type := oct2int('0300'O);
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200670 mncc.u.rtp.is_ipv6 := f_addr_is_ipv6(cp.cn_rtp_addr);
671 mncc.u.rtp.ip := f_addrstr2addr(cp.cn_rtp_addr);
672 mncc.u.rtp.rtp_port := cp.cn_rtp_port;
673 MNCC.send(mncc);
Harald Welteddf011e2019-04-19 09:39:35 +0200674 }
675
676 /* OSC -> SIP: We should never receive INVITE */
677 timer T := 10.0;
678 T.start;
679 alt {
680 [] SIP.receive(tr_SIP_INVITE(?, sip_addr_gsm, sip_addr_ext, ?, ?)) {
681 setverdict(fail, "Received unexpected INVITE");
682 }
683 [] T.timeout {
684 setverdict(pass);
685 }
686 }
687}
688testcase TC_mo_setup_disc_late_rtp() runs on test_CT {
689 var ConnHdlrPars pars;
690 var ConnHdlr vc_conn;
691 f_init();
692 pars := valueof(t_Pars);
Neels Hofmeyra0d015b2023-09-13 04:32:14 +0200693 pars.g_cp := valueof(t_CallPars(is_mo := true, mncc_with_sdp := false));
Harald Welteddf011e2019-04-19 09:39:35 +0200694 vc_conn := f_start_handler(refers(f_TC_mo_setup_disc_late_rtp), pars);
695 vc_conn.done;
696}
Harald Welteafec4712018-03-19 22:52:17 +0100697
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200698testcase TC_mt_with_sdp() runs on test_CT {
699 var ConnHdlrPars pars;
700 var ConnHdlr vc_conn;
701 f_init();
702 pars := valueof(t_Pars);
703 pars.g_cp := valueof(t_CallPars(is_mo := false, mncc_with_sdp := true));
704 vc_conn := f_start_handler(refers(f_TC_mt_success_rel_gsm), pars);
705 vc_conn.done;
706}
707
708testcase TC_mo_with_sdp() runs on test_CT {
709 var ConnHdlrPars pars;
710 var ConnHdlr vc_conn;
711 f_init();
712 pars := valueof(t_Pars);
713 pars.g_cp := valueof(t_CallPars(is_mo := true, mncc_with_sdp := true));
714 vc_conn := f_start_handler(refers(f_TC_mo_success_rel_sip), pars);
715 vc_conn.done;
716}
717
Harald Welteafec4712018-03-19 22:52:17 +0100718control {
Harald Welteb0d93602018-03-20 18:09:34 +0100719 execute( TC_mt_success_rel_gsm() );
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200720 execute( TC_mt_success_rel_gsm_ipv6() );
Harald Welteb0d93602018-03-20 18:09:34 +0100721 execute( TC_mt_success_rel_sip() );
722 execute( TC_mo_success_rel_gsm() );
Pau Espin Pedrolbdd874a2020-09-10 19:09:47 +0200723 execute( TC_mo_success_rel_gsm_ipv6() );
Harald Welteb0d93602018-03-20 18:09:34 +0100724 execute( TC_mo_success_rel_sip() );
Harald Welteddf011e2019-04-19 09:39:35 +0200725 execute( TC_mo_setup_disc_late_rtp() );
Neels Hofmeyr2e3a0e22023-09-12 02:09:48 +0200726 execute( TC_mt_with_sdp() );
727 execute( TC_mo_with_sdp() );
Harald Welteafec4712018-03-19 22:52:17 +0100728}
729
730
731
732}