summaryrefslogtreecommitdiffstats
path: root/sip/SIP_Tests.ttcn
blob: 9de272331788dc23abd26e4fc5fdf81e32bebabe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
module SIP_Tests {

/* osmo-sip-connector test suite in TTCN-3
 * (C) 2018-2019 Harald Welte <laforge@gnumonks.org>
 * All rights reserved.
 *
 * Released under the terms of GNU General Public License, Version 2 or
 * (at your option) any later version.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

import from General_Types all;
import from Osmocom_Types all;

import from Osmocom_CTRL_Functions all;
import from Osmocom_CTRL_Types all;
import from Osmocom_CTRL_Adapter all;

import from TELNETasp_PortType all;
import from Osmocom_VTY_Functions all;

import from MNCC_Emulation all;
import from MNCC_Types all;

import from SDP_Types all;

import from SIP_Emulation all;
import from SIPmsg_Types all;
import from SIP_Templates all;

modulepar {
	charstring mp_local_host := "127.0.0.2";
	charstring mp_osmosip_host := "127.0.0.1";
	integer mp_osmosip_port_ctrl := -1; /* RFU */
	charstring mp_mncc := "/tmp/mncc";
}

type component test_CT extends CTRL_Adapter_CT {
	var MNCC_Emulation_CT vc_MNCC;
	var SIP_Emulation_CT vc_SIP;

	port TELNETasp_PT SIPVTY;
}

type component ConnHdlr extends SIP_ConnHdlr, MNCC_ConnHdlr {
	var ConnHdlrPars g_pars;
	timer g_Tguard;
}

type record ConnHdlrPars {
	float t_guard,
	CallPars g_cp optional
}

type record CallPars {
	boolean is_mo,
	charstring calling,
	charstring called,

	uint32_t mncc_call_id optional,

	CallParsComputed comp optional
}

type record CallParsComputed {
	CallidString sip_call_id,
	SipAddr sip_url_ext,
	SipAddr sip_url_gsm,
	charstring sip_body,
	integer sip_seq_nr
}

private template (value) CallPars t_CallPars(boolean is_mo) := {
	is_mo := is_mo,
	calling := "12345",
	called := "98766",
	mncc_call_id := omit,
	comp := omit
}

private function f_CallPars_compute(inout CallPars cp) {
	if (cp.is_mo) {
		cp.comp.sip_url_ext := valueof(ts_SipAddr(cp.called, mp_local_host, 5060));
		cp.comp.sip_url_gsm := valueof(ts_SipAddr(cp.calling, mp_osmosip_host, 5060));
		cp.mncc_call_id := f_rnd_int(429496725);
	} else {
		cp.comp.sip_url_ext := valueof(ts_SipAddr(cp.calling, mp_local_host, 5060));
		cp.comp.sip_url_gsm := valueof(ts_SipAddr(cp.called, mp_osmosip_host, 5060));
		cp.comp.sip_call_id := hex2str(f_rnd_hexstring(15));
	}
	cp.comp.sip_seq_nr := f_rnd_int(4294967295);
	cp.comp.sip_body := "";
}


function f_init_mncc(charstring id) runs on test_CT {
	id := id & "-MNCC";
	var MnccOps ops := {
		create_cb := refers(MNCC_Emulation.ExpectedCreateCallback),
		unitdata_cb := refers(MNCC_Emulation.DummyUnitdataCallback)
	};

	vc_MNCC := MNCC_Emulation_CT.create(id);
	map(vc_MNCC:MNCC, system:MNCC_CODEC_PT);
	vc_MNCC.start(MNCC_Emulation.main(ops, id, mp_mncc, true));
}

function f_init() runs on test_CT {
	//f_ipa_ctrl_start(mp_osmosip_host, mp_osmosip_port_ctrl);
	f_init_mncc("SIP_Test");
	log("end of f_init_mncc");
	f_init_sip(vc_SIP, "SIP_Test");
	log("end of f_init_sip");

	map(self:SIPVTY, system:SIPVTY);
	f_vty_set_prompts(SIPVTY);
	f_vty_transceive(SIPVTY, "enable");
	log("end of f_init");
}

type function void_fn(charstring id) runs on ConnHdlr;

function f_start_handler(void_fn fn, ConnHdlrPars pars)
runs on test_CT return ConnHdlr {
	var ConnHdlr vc_conn;
	var charstring id := testcasename();

	vc_conn := ConnHdlr.create(id);

	connect(vc_conn:SIP, vc_SIP:CLIENT);
	connect(vc_conn:SIP_PROC, vc_SIP:CLIENT_PROC);

	connect(vc_conn:MNCC, vc_MNCC:MNCC_CLIENT);
	connect(vc_conn:MNCC_PROC, vc_MNCC:MNCC_PROC);

	vc_conn.start(f_handler_init(fn, id, pars));
	return vc_conn;
}

private altstep as_Tguard() runs on ConnHdlr {
	[] g_Tguard.timeout {
		setverdict(fail, "Tguard timeout");
		mtc.stop;
	}
}

private function f_handler_init(void_fn fn, charstring id, ConnHdlrPars pars)
runs on ConnHdlr {
	g_pars := pars;
	g_Tguard.start(pars.t_guard);
	activate(as_Tguard());

	/* call the user-supied test case function */
	fn.apply(id);
}


template (value) ConnHdlrPars t_Pars := {
	t_guard := 30.0,
	g_cp := omit
}

/* Establish a mobile terminated call described in 'cp' */
function f_establish_mt(inout CallPars cp) runs on ConnHdlr {
	var template SipAddr sip_addr_gsm := tr_SipAddr_from_val(cp.comp.sip_url_gsm);
	var template SipAddr sip_addr_ext := tr_SipAddr_from_val(cp.comp.sip_url_ext);
	var PDU_SIP_Request sip_req;
	var MNCC_PDU mncc;

	/* Ask MNCC_Emulation to "expect" a call to the given called number */
	f_create_mncc_expect(cp.called);

	/* OSC <- SIP: A party sends SIP invite for a MT-call into OSC */
	SIP.send(ts_SIP_INVITE(cp.comp.sip_call_id, cp.comp.sip_url_ext, cp.comp.sip_url_gsm,
				cp.comp.sip_seq_nr, cp.comp.sip_body));
	/* MSC <- OSC: OSC generates MNCC_SETUP_REQ from INVITE */
	MNCC.receive(tr_MNCC_SETUP_req) -> value mncc {
		cp.mncc_call_id := mncc.u.signal.callref;
		}
	/* OSC -> SIP */
	SIP.receive(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, *,
				    "INVITE", 100, ?, "Trying", *));

	/* MSC -> OSC: After MS sends CALL CONF in response to SETUP */
	MNCC.send(ts_MNCC_CALL_CONF_ind(cp.mncc_call_id));
	/* MSC <- OSC: OSC asks MSC to create RTP socket */
	MNCC.receive(tr_MNCC_RTP_CREATE(cp.mncc_call_id));
	MNCC.send(ts_MNCC_RTP_CREATE(cp.mncc_call_id));

	/* MSC -> OSC: After MS is ringing and sent CC ALERTING */
	MNCC.send(ts_MNCC_ALERT_ind(cp.mncc_call_id));
	SIP.clear;
	SIP.receive(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, *,
				    "INVITE", 180, ?, "Ringing", *));

	/* MSC -> OSC: After MT user has picked up and sent CC CONNECT */
	MNCC.send(ts_MNCC_SETUP_CNF(cp.mncc_call_id));

	SIP.clear;
	interleave {
	/* MSC <- OSC: OSC asks MSC to connect its RTP stream to remote end */
	[] MNCC.receive(tr_MNCC_RTP_CONNECT(cp.mncc_call_id)) {}
	/* OSC -> SIP: OSC confirms call establishment to SIP side */
	[] SIP.receive(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, ?,
					"INVITE", 200, ?, "OK", ?)) {}
	}
	/* OSC <- SIP: SIP world acknowledges "200 OK" */
	SIP.send(ts_SIP_ACK(cp.comp.sip_call_id, cp.comp.sip_url_ext, cp.comp.sip_url_gsm,
			    cp.comp.sip_seq_nr, omit));
	/* MSC <- OSC: OSC sends SETUP COMPL to MNCC (which triggers CC CONNECT ACK */
	MNCC.receive(tr_MNCC_SETUP_COMPL_req(cp.mncc_call_id));
}

/* Establish a mobile originated call described in 'cp' */
function f_establish_mo(inout CallPars cp) runs on ConnHdlr {
	var MNCC_number dst := valueof(ts_MNCC_number(cp.called, GSM48_TON_UNKNOWN));
	var MNCC_number src := valueof(ts_MNCC_number(cp.calling, GSM48_TON_UNKNOWN));
	var template SipAddr sip_addr_gsm := tr_SipAddr_from_val(cp.comp.sip_url_gsm);
	var template SipAddr sip_addr_ext := tr_SipAddr_from_val(cp.comp.sip_url_ext);
	var PDU_SIP_Request sip_req;
	var integer seq_nr;

	f_create_sip_expect(cp.comp.sip_url_ext.addr.nameAddr.addrSpec);

	/* MSC -> OSC: MSC sends SETUP.ind after CC SETUP was received from MS */
	MNCC.send(ts_MNCC_SETUP_ind(cp.mncc_call_id, dst, src, "262420123456789"));
	/* MSC <- OSC: Create GSM side RTP socket */
	MNCC.receive(tr_MNCC_RTP_CREATE(cp.mncc_call_id)) {
		var MNCC_PDU mncc := valueof(ts_MNCC_RTP_CREATE(cp.mncc_call_id));
		mncc.u.rtp.payload_msg_type := oct2int('0300'O);
		MNCC.send(mncc);	/* FIXME: port/ip */
		}
	/* OSC -> SIP: Send INVITE with GSM side IP/Port in SDP */
	SIP.receive(tr_SIP_INVITE(?, sip_addr_gsm, sip_addr_ext, ?, ?)) -> value sip_req {
		cp.comp.sip_url_gsm.params := sip_req.msgHeader.fromField.fromParams;
		cp.comp.sip_call_id := sip_req.msgHeader.callId.callid;
		seq_nr := sip_req.msgHeader.cSeq.seqNumber;
		}
	/* OSC <- SIP: Notify call is proceeding */
	SIP.send(ts_SIP_Response(cp.comp.sip_call_id, cp.comp.sip_url_gsm, cp.comp.sip_url_ext,
				 "INVITE", 100, seq_nr, "Trying", sip_req.msgHeader.via));
	/* MSC <- OSC: "100 Trying" translated to MNCC_CALL_PROC_REQ */
	MNCC.receive(tr_MNCC_CALL_PROC_req(cp.mncc_call_id));

	/* OSC <- SIP: SIP-terminated user is ringing now */
	SIP.send(ts_SIP_Response(cp.comp.sip_call_id, cp.comp.sip_url_gsm, cp.comp.sip_url_ext,
				 "INVITE", 180, seq_nr, "Ringing", sip_req.msgHeader.via));

	/* MSC <- OSC: "180 Ringing" translated to MNCC_ALERT_REQ */
	MNCC.receive(tr_MNCC_ALERT_req(cp.mncc_call_id)) {}

	/* OSC <- SIP: SIP-terminated user has accepted the call */
	SIP.send(ts_SIP_Response(cp.comp.sip_call_id, cp.comp.sip_url_gsm, cp.comp.sip_url_ext,
				 "INVITE", 200, seq_nr, "OK", sip_req.msgHeader.via,
				 cp.comp.sip_body));
	MNCC.receive(tr_MNCC_RTP_CONNECT(cp.mncc_call_id));
	/* MSC <- OSC: "200 OK" translated to MNCC_SETUP_RSP */
	MNCC.receive(tr_MNCC_SETUP_rsp(cp.mncc_call_id));

	/* MSC -> OSC: CC CONNECT ACK was received from MS */
	MNCC.send(ts_MNCC_SETUP_COMPL_ind(cp.mncc_call_id));
	/* OSC -> SIP: Acknowledge the call */
	SIP.receive(tr_SIP_ACK(cp.comp.sip_call_id, sip_addr_gsm, sip_addr_ext, ?, omit));
}

/* Release call from the mobile side */
function f_release_mobile(inout CallPars cp) runs on ConnHdlr {
	var template SipAddr sip_addr_gsm := tr_SipAddr_from_val(cp.comp.sip_url_gsm);
	var template SipAddr sip_addr_ext := tr_SipAddr_from_val(cp.comp.sip_url_ext);
	var PDU_SIP_Request sip_req;
	SIP.clear;
	/* MSC -> OSC: Simulate a CC DISCONNET from the MT user */
	MNCC.send(ts_MNCC_DISC_ind(cp.mncc_call_id, ts_MNCC_cause(0)));
	/* OSC -> SIP: Expect BYE from OSC to SIP side */
	SIP.receive(tr_SIP_BYE(cp.comp.sip_call_id, sip_addr_gsm, sip_addr_ext, ?, *)) -> value sip_req {
		cp.comp.sip_url_gsm.params := sip_req.msgHeader.fromField.fromParams;
	}
	/* OSC <- SIP: Acknowledge the BYE */
	SIP.send(ts_SIP_Response(cp.comp.sip_call_id, cp.comp.sip_url_gsm, cp.comp.sip_url_ext,
					 "BYE", 200, sip_req.msgHeader.cSeq.seqNumber, "OK",
					 sip_req.msgHeader.via));
	/* MSC <- OSC: Send REL_REQ to MSC, triggers CC RELEASE REQ to MS */
	MNCC.receive(tr_MNCC_REL_req(cp.mncc_call_id)); // CAUSE?
	/* MSC -> OSC: MS has responded with CC CLEAR COMPL, triggers MNCC_REL_CNF */
	MNCC.send(ts_MNCC_REL_cnf(cp.mncc_call_id, ts_MNCC_cause(0)));
}

/* Release call from the SIP side */
function f_release_sip(inout CallPars cp) runs on ConnHdlr {
	var template SipAddr sip_addr_gsm := tr_SipAddr_from_val(cp.comp.sip_url_gsm);
	var template SipAddr sip_addr_ext := tr_SipAddr_from_val(cp.comp.sip_url_ext);
	/* OSC <- SIP: SIP-side sends a BYE to OSC */
	SIP.send(ts_SIP_BYE(cp.comp.sip_call_id, cp.comp.sip_url_ext, cp.comp.sip_url_gsm,
			    cp.comp.sip_seq_nr, omit));
	/* MSC <- OSC: Expect OSC to cause MNCC Disconnect Request */
	MNCC.receive(tr_MNCC_DISC_req(cp.mncc_call_id));
	/* MSC -> OSC: Indicate GSM side release */
	MNCC.send(ts_MNCC_REL_ind(cp.mncc_call_id, ts_MNCC_cause(0)));
	/* OSC -> SIP: Confirmation to SIP side */
	SIP.receive(tr_SIP_Response(cp.comp.sip_call_id, sip_addr_ext, sip_addr_gsm, *,
				    "BYE", 200, cp.comp.sip_seq_nr, "OK", omit));
}

/* Successful MT Call, which is subsequently released by GSM side */
private function f_TC_mt_success_rel_gsm(charstring id) runs on ConnHdlr {
	var CallPars cp := valueof(t_CallPars(false));
	f_CallPars_compute(cp);
	cp.comp.sip_body := "v=0\r\no=Osmocom 0 0 IN IP4 0.0.0.0\r\ns=GSM Call\r\nc=IN IP4 0.0.0.0\r\nt=0 0\r\nm=audio 0 RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
	f_sleep(3.0)

	f_establish_mt(cp);
	/* now call is fully established */
	f_sleep(2.0);
	f_release_mobile(cp);
	setverdict(pass);
}
testcase TC_mt_success_rel_gsm() runs on test_CT {
	var ConnHdlrPars pars;
	var ConnHdlr vc_conn;
	f_init();
	pars := valueof(t_Pars);
	vc_conn := f_start_handler(refers(f_TC_mt_success_rel_gsm), pars);
	vc_conn.done;
}

/* Successful MT Call, which is subsequently released by SIP side */
private function f_TC_mt_success_rel_sip(charstring id) runs on ConnHdlr {
	var CallPars cp := valueof(t_CallPars(false));
	f_CallPars_compute(cp);
	cp.comp.sip_body := "v=0\r\no=Osmocom 0 0 IN IP4 0.0.0.0\r\ns=GSM Call\r\nc=IN IP4 0.0.0.0\r\nt=0 0\r\nm=audio 0 RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
	f_sleep(3.0)

	f_establish_mt(cp);
	/* now call is fully established */
	f_sleep(2.0);
	f_release_sip(cp);
	setverdict(pass);
}
testcase TC_mt_success_rel_sip() runs on test_CT {
	var ConnHdlrPars pars;
	var ConnHdlr vc_conn;
	f_init();
	pars := valueof(t_Pars);
	vc_conn := f_start_handler(refers(f_TC_mt_success_rel_sip), pars);
	vc_conn.done;
}


/* Successful MO Call, which is subsequently released by GSM side */
private function f_TC_mo_success_rel_gsm(charstring id) runs on ConnHdlr {
	var CallPars cp := valueof(t_CallPars(true));
	f_CallPars_compute(cp);
	cp.comp.sip_body := "v=0\r\no=Osmocom 0 0 IN IP4 0.0.0.0\r\ns=GSM Call\r\nc=IN IP4 0.0.0.0\r\nt=0 0\r\nm=audio 0 RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
	f_sleep(3.0)

	f_establish_mo(cp);
	/* now call is fully established */
	f_sleep(2.0);
	f_release_mobile(cp);
	setverdict(pass);
}
testcase TC_mo_success_rel_gsm() runs on test_CT {
	var ConnHdlrPars pars;
	var ConnHdlr vc_conn;
	f_init();
	pars := valueof(t_Pars);
	vc_conn := f_start_handler(refers(f_TC_mo_success_rel_gsm), pars);
	vc_conn.done;
}

/* Successful MO Call, which is subsequently released by SIP side */
private function f_TC_mo_success_rel_sip(charstring id) runs on ConnHdlr {
	var CallPars cp := valueof(t_CallPars(true));
	f_CallPars_compute(cp);
	cp.comp.sip_body := "v=0\r\no=Osmocom 0 0 IN IP4 0.0.0.0\r\ns=GSM Call\r\nc=IN IP4 0.0.0.0\r\nt=0 0\r\nm=audio 0 RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
	f_sleep(3.0)

	f_establish_mo(cp);
	/* now call is fully established */
	f_sleep(2.0);
	f_release_sip(cp);
	setverdict(pass);
}
testcase TC_mo_success_rel_sip() runs on test_CT {
	var ConnHdlrPars pars;
	var ConnHdlr vc_conn;
	f_init();
	pars := valueof(t_Pars);
	vc_conn := f_start_handler(refers(f_TC_mo_success_rel_sip), pars);
	vc_conn.done;
}

/* SETUP followed by DISC results in lingering B-leg (OS#3518)*/
private function f_TC_mo_setup_disc_late_rtp(charstring id) runs on ConnHdlr {
	var CallPars cp := valueof(t_CallPars(true));
	f_CallPars_compute(cp);
	cp.comp.sip_body := "v=0\r\no=Osmocom 0 0 IN IP4 0.0.0.0\r\ns=GSM Call\r\nc=IN IP4 0.0.0.0\r\nt=0 0\r\nm=audio 0 RTP/AVP 0\r\na=rtpmap:0 GSM/8000\r\n";
	f_sleep(3.0);

	var MNCC_number dst := valueof(ts_MNCC_number(cp.called, GSM48_TON_UNKNOWN));
	var MNCC_number src := valueof(ts_MNCC_number(cp.calling, GSM48_TON_UNKNOWN));
	var template SipAddr sip_addr_gsm := tr_SipAddr_from_val(cp.comp.sip_url_gsm);
	var template SipAddr sip_addr_ext := tr_SipAddr_from_val(cp.comp.sip_url_ext);

	f_create_sip_expect(cp.comp.sip_url_ext.addr.nameAddr.addrSpec);

	/* MSC -> OSC: MSC sends SETUP.ind after CC SETUP was received from MS */
	MNCC.send(ts_MNCC_SETUP_ind(cp.mncc_call_id, dst, src, "262420123456789"));

	/* MSC -> OSC: Simulate a CC DISCONNET from the MT user *before* responding to the RTP_CREATE */
	MNCC.send(ts_MNCC_DISC_ind(cp.mncc_call_id, ts_MNCC_cause(0)));

	/* MSC <- OSC: Create GSM side RTP socket (too late) */
	MNCC.receive(tr_MNCC_RTP_CREATE(cp.mncc_call_id)) {
		var MNCC_PDU mncc := valueof(ts_MNCC_RTP_CREATE(cp.mncc_call_id));
		mncc.u.rtp.payload_msg_type := oct2int('0300'O);
		MNCC.send(mncc);	/* FIXME: port/ip */
		}

	/* OSC -> SIP: We should never receive INVITE */
	timer T := 10.0;
	T.start;
	alt {
	[] SIP.receive(tr_SIP_INVITE(?, sip_addr_gsm, sip_addr_ext, ?, ?)) {
		setverdict(fail, "Received unexpected INVITE");
		}
	[] T.timeout {
		setverdict(pass);
		}
	}
}
testcase TC_mo_setup_disc_late_rtp() runs on test_CT {
	var ConnHdlrPars pars;
	var ConnHdlr vc_conn;
	f_init();
	pars := valueof(t_Pars);
	vc_conn := f_start_handler(refers(f_TC_mo_setup_disc_late_rtp), pars);
	vc_conn.done;
}

control {
	execute( TC_mt_success_rel_gsm() );
	execute( TC_mt_success_rel_sip() );
	execute( TC_mo_success_rel_gsm() );
	execute( TC_mo_success_rel_sip() );
	execute( TC_mo_setup_disc_late_rtp() );
}



}