blob: afd5f33036e5cd97be62334fd8a5e1a1a11d06fe [file] [log] [blame]
Harald Welte00a067f2017-09-13 23:27:17 +02001module MGCP_Test {
2 import from MGCP_Types all;
Harald Welte3c6ebb92017-09-16 00:56:57 +08003 import from SDP_Types all;
4 import from MGCP_CodecPort all;
5 import from MGCP_CodecPort_CtrlFunct all;
6 import from IPL4asp_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +02007
8 type component dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +08009 port MGCP_CODEC_PT MGCP;
10 var boolean initialized := false;
11 var ConnectionId g_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080012 var integer g_trans_id;
Harald Welte00a067f2017-09-13 23:27:17 +020013 };
14
Harald Weltee1e18c52017-09-17 16:23:07 +080015 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
16 var MgcpTransId tid := int2str(g_trans_id);
17 g_trans_id := g_trans_id + 1;
18 return tid;
19 }
20
Harald Welte3c6ebb92017-09-16 00:56:57 +080021 modulepar {
22 PortNumber mp_local_udp_port := 2727;
23 charstring mp_local_ip := "127.0.0.1";
24 PortNumber mp_remote_udp_port := 2427;
25 charstring mp_remote_ip := "127.0.0.1";
26 }
27
28 private function f_init()runs on dummy_CT {
29 var Result res;
30 if (initialized == true) {
31 return;
32 }
33 initialized := true;
34
Harald Weltee1e18c52017-09-17 16:23:07 +080035 g_trans_id := float2int(rnd()*65535.0);
Harald Welte3c6ebb92017-09-16 00:56:57 +080036 map(self:MGCP, system:MGCP_CODEC_PT);
37 res := f_IPL4_connect(MGCP, mp_remote_ip, mp_remote_udp_port, mp_local_ip, mp_local_udp_port, 0, { udp := {} });
38 g_conn_id := res.connId;
39 }
40
41 /* 3.2.2.6 Connection Mode (sendonly, recvonly, sendrecv, confrnce, inactive, loopback,
42 * conttest, netwloop, netwtest) */
43 template MgcpParameter t_MgcpParConnMode(template MgcpConnectionMode mode) := { "M", mode };
44
45 /* 3.2.2.2 CallId: maximum 32 hex chars */
46 template MgcpParameter ts_MgcpParCallId(MgcpCallId cid) := {
47 code := "C",
48 val := hex2str(cid)
49 };
50
51 /* 3.2.2.18 RequestIdentifier: Maximum 32 hex chars */
52 template MgcpParameter ts_MgcpParReqId(MgcpRequestId rid) := {
53 code := "X",
54 val := hex2str(rid)
55 };
56
57 /* 3.2.2.10: LocalConnectionOptions (codec, packetization, bandwidth, ToS, eco, gain, silence, ...) */
58 template MgcpParameter t_MgcpParLocConnOpt(template charstring lco) := { "L", lco };
59
60 /* 3.2.2.5: ConnectionId: maximum 32 hex chars */
61 template MgcpParameter ts_MgcpParConnectionId(MgcpConnectionId cid) := {
62 code := "I",
63 val := hex2str(cid)
64 };
65
66 /* osmo-bsc_mgcp implements L/C/M/X only, osmo-mgw adds 'I' */
67 /* SDP: osmo-bsc_mgcp implements Tx of v,o,s,c,t,m,a */
68
Harald Weltee636afd2017-09-17 16:24:09 +080069 template MgcpResponse tr_MgcpResp_Err(template MgcpResponseCode code) := {
70 line := {
71 code := code,
72 trans_id := ?,
73 string := ?
74 },
75 params := {},
76 sdp := omit
77 }
78
Harald Welte3c6ebb92017-09-16 00:56:57 +080079 template MgcpCommandLine t_MgcpCmdLine(template charstring verb, template MgcpTransId trans_id, template charstring ep) := {
80 verb := verb,
81 trans_id := trans_id,
82 ep := ep,
83 ver := "1.0"
84 };
85
86 template MgcpCommand ts_CRCX(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, template SDP_Message sdp := omit) := {
87 line := t_MgcpCmdLine("CRCX", trans_id, ep),
88 params := {
89 t_MgcpParConnMode(mode),
90 ts_MgcpParCallId(call_id),
91 //t_MgcpParReqId(omit),
92 t_MgcpParLocConnOpt("p: 20")
93 },
94 sdp := sdp
95 }
96
Harald Weltee636afd2017-09-17 16:24:09 +080097 template MgcpCommand ts_MDCX(MgcpTransId trans_id, charstring ep, MgcpConnectionMode mode, MgcpCallId call_id, template SDP_Message sdp := omit) := {
98 line := t_MgcpCmdLine("MDCX", trans_id, ep),
99 params := {
100 t_MgcpParConnMode(mode),
101 ts_MgcpParCallId(call_id),
102 //t_MgcpParReqId(omit),
103 t_MgcpParLocConnOpt("p: 20")
104 },
105 sdp := sdp
106 }
107
108 template MgcpCommand ts_DLCX(MgcpTransId trans_id, charstring ep, MgcpCallId call_id) := {
109 line := t_MgcpCmdLine("DLCX", trans_id, ep),
110 params := {
111 ts_MgcpParCallId(call_id)
112 },
113 sdp := omit
114 }
115
116 /* SDP Templates */
117 template SDP_Origin ts_SDP_origin(charstring addr, charstring session_id,
118 charstring session_version := "1",
119 charstring addr_type := "IP4",
120 charstring user_name := "-") := {
121 user_name := user_name,
122 session_id := session_id,
123 session_version := session_version,
124 net_type := "IN",
125 addr_type := addr_type,
126 addr := addr
127 }
128
129 template SDP_connection ts_SDP_connection_IP(charstring addr, charstring addr_type := "IP4",
130 template integer ttl := omit,
131 template integer num_of_addr := omit) :={
132 net_type := "IN",
133 addr_type := addr_type,
134 conn_addr := {
135 addr := addr,
136 ttl := ttl,
137 num_of_addr := num_of_addr
138 }
139 }
140
141 template SDP_time ts_SDP_time(charstring beg, charstring end) := {
142 time_field := {
143 start_time := beg,
144 stop_time := end
145 },
146 time_repeat := omit
147 }
148
149 template SDP_media_desc ts_SDP_media_desc(integer port_number, SDP_fmt_list fmts,
150 SDP_attribute_list attributes) := {
151 media_field := {
152 media := "audio",
153 ports := {
154 port_number := port_number,
155 num_of_ports := omit
156 },
157 transport := "ARTP/AVP",
158 fmts := fmts
159 },
160 information := omit,
161 connections := omit,
162 bandwidth := omit,
163 key := omit,
164 attributes := attributes
165 }
166
167 template SDP_Message ts_SDP(charstring local_addr, charstring remote_addr,
168 charstring session_id, charstring session_version,
169 integer rtp_port, SDP_fmt_list fmts,
170 SDP_attribute_list attributes) := {
171 protocol_version := 0,
172 origin := ts_SDP_origin(local_addr, session_id, session_version),
173 session_name := "-",
174 information := omit,
175 uri := omit,
176 emails := omit,
177 phone_numbers := omit,
178 connection := ts_SDP_connection_IP(remote_addr),
179 bandwidth := omit,
180 times := { ts_SDP_time("0","0") },
181 timezone_adjustments := omit,
182 key := omit,
183 attributes := omit,
184 media_list := { ts_SDP_media_desc(rtp_port, fmts, attributes) }
185 }
186
187 template SDP_attribute ts_SDP_rtpmap(integer fmt, charstring val) := {
188 rtpmap := {
189 attr_value := int2str(fmt) & " " & val
190 }
191 }
192 template SDP_attribute ts_SDP_ptime(integer p) := {
193 ptime := {
194 attr_value := int2str(p)
195 }
196 }
197
Harald Welte00a067f2017-09-13 23:27:17 +0200198 testcase TC_selftest() runs on dummy_CT {
199 const charstring c_auep := "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n";
200 const charstring c_mdcx3 := "MDCX 18983215 1@mgw MGCP 1.0\r\n";
201 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
202 "I: 1\n" &
203 "\n" &
204 "v=0\r\n" &
205 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
206 "s=-\r\n" &
207 "c=IN IP4 0.0.0.0\r\n" &
208 "t=0 0\r\n" &
209 "m=audio 0 RTP/AVP 126\r\n" &
210 "a=rtpmap:126 AMR/8000\r\n" &
211 "a=ptime:20\r\n";
212 const charstring c_mdcx4 := "MDCX 18983216 1@mgw MGCP 1.0\r\n" &
213 "M: sendrecv\r" &
214 "C: 2\r\n" &
215 "I: 1\r\n" &
216 "L: p:20, a:AMR, nt:IN\r\n" &
217 "\n" &
218 "v=0\r\n" &
219 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800220 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200221 "c=IN IP4 0.0.0.0\r\n" &
222 "t=0 0\r\n" &
223 "m=audio 4441 RTP/AVP 99\r\n" &
224 "a=rtpmap:99 AMR/8000\r\n" &
225 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800226 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200227
228 log(c_auep);
229 log(dec_MgcpCommand(c_auep));
230
231 log(c_mdcx3);
232 log(dec_MgcpCommand(c_mdcx3));
233
234 log(c_mdcx3_ret);
235 log(dec_MgcpResponse(c_mdcx3_ret));
236
237 log(c_mdcx4);
238 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800239
240 log(ts_CRCX("23", "42@mgw", "sendrecv", '1234'H));
241 log(enc_MgcpCommand(valueof(ts_CRCX("23", "42@mgw", "sendrecv", '1234'H))));
242
243 log(c_crcx510_ret);
244 log(dec_MgcpResponse(c_crcx510_ret));
245 log(dec_MgcpMessage(c_crcx510_ret));
246 }
247
Harald Weltee636afd2017-09-17 16:24:09 +0800248 /* CRCX test ideas:
249 * - without mandatory CallId
250 * - without mandatory ConnectionId
251 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
252 * - CRCX with remote session description and without
253 *
254 * general ideas:
255 * - packetization != 20ms
256 * - invalid mode
257 * x unsupported mode (517)
258 * x bidirectional mode before RemoteConnDesc: 527
259 * - invalid codec
260 * - retransmission of same transaction
261 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
262 */
263
264 /* build a receive template for receiving a MGCP message */
265 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
266 var template MGCP_RecvFrom mrf := {
267 connId := g_conn_id,
268 remName := mp_remote_ip,
269 remPort := mp_remote_udp_port,
270 locName := mp_local_ip,
271 locPort := mp_local_udp_port,
272 msg := { response := resp }
273 }
274 return mrf;
275 }
276
277 /* Send a MGCP request + receive a (matching!) response */
278 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
279 var MgcpMessage msg := { command := valueof(cmd) };
280 resp.line.trans_id := cmd.line.trans_id;
281 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800282 var MGCP_RecvFrom mrf;
283 timer T := 5.0;
284
Harald Welte3c6ebb92017-09-16 00:56:57 +0800285 MGCP.send(t_MGCP_Send(g_conn_id, msg));
286 T.start;
287 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800288 [] MGCP.receive(mrt) -> value mrf { }
289 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) { setverdict(fail); }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800290 [] MGCP.receive { repeat; }
291 [] T.timeout { setverdict(fail); }
292 }
293 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800294
295 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
296 return mrf.msg.response;
297 } else {
298 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
299 return r;
300 }
Harald Welte00a067f2017-09-13 23:27:17 +0200301 }
302
Harald Weltee636afd2017-09-17 16:24:09 +0800303 /* test valid CRCX without SDP */
304 testcase TC_crcx() runs on dummy_CT {
305 var template MgcpCommand cmd;
306 var MgcpResponse resp;
307 var template MgcpResponse rtmpl := {
308 line := {
309 code := "200",
310 string := "OK"
311 },
312 params:= ?,
313 sdp := ?
314 };
315
316 f_init();
317
318 cmd := ts_CRCX(get_next_trans_id(), "2@mgw", "sendrecv", '1234'H);
319 resp := mgcp_transceive_mgw(cmd, rtmpl);
320 setverdict(pass);
321 }
322
323 /* test CRCX with unsupported mode, expect 517 */
324 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
325 var template MgcpCommand cmd;
326 var MgcpResponse resp;
327 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
328
329 f_init();
330
331 cmd := ts_CRCX(get_next_trans_id(), "2@mgw", "netwtest", '1234'H);
332 resp := mgcp_transceive_mgw(cmd, rtmpl);
333 setverdict(pass);
334 }
335
336 /* test CRCX with early bi-directional mode, expect 527 */
337 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
338 var template MgcpCommand cmd;
339 var MgcpResponse resp;
340 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
341
342 f_init();
343
344 cmd := ts_CRCX(get_next_trans_id(), "2@mgw", "sendrecv", '1234'H);
345 resp := mgcp_transceive_mgw(cmd, rtmpl);
346 setverdict(pass);
347 }
348
349 /* test CRCX with unsupported Parameters */
350 testcase TC_crcx_unsupp_param() runs on dummy_CT {
351 var template MgcpCommand cmd;
352 var MgcpResponse resp;
353 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
354
355 f_init();
356
357 cmd := ts_CRCX(get_next_trans_id(), "2@mgw", "recvonly", '1234'H);
358 cmd.params := {
359 t_MgcpParConnMode("recvonly"),
360 ts_MgcpParCallId('1234'H),
361 t_MgcpParLocConnOpt("p:20"),
362 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
363 { "N", "foobar" }
364 }
365 resp := mgcp_transceive_mgw(cmd, rtmpl);
366 setverdict(pass);
367 }
368
369 /* test CRCX with missing CallId */
370 testcase TC_crcx_missing_callid() runs on dummy_CT {
371 var template MgcpCommand cmd;
372 var MgcpResponse resp;
373 var template MgcpResponse rtmpl := tr_MgcpResp_Err("400");
374
375 f_init();
376
377 cmd := ts_CRCX(get_next_trans_id(), "2@mgw", "recvonly", '1234'H);
378 cmd.params := {
379 t_MgcpParConnMode("recvonly"),
380 t_MgcpParLocConnOpt("p:20")
381 }
382 resp := mgcp_transceive_mgw(cmd, rtmpl);
383 setverdict(pass);
384 }
385
386 /* test CRCX with missing Mode */
387 testcase TC_crcx_missing_mode() runs on dummy_CT {
388 var template MgcpCommand cmd;
389 var MgcpResponse resp;
390 var template MgcpResponse rtmpl := tr_MgcpResp_Err("400");
391
392 f_init();
393
394 cmd := ts_CRCX(get_next_trans_id(), "2@mgw", "recvonly", '1234'H);
395 cmd.params := {
396 ts_MgcpParCallId('1234'H),
397 t_MgcpParLocConnOpt("p:20")
398 }
399 resp := mgcp_transceive_mgw(cmd, rtmpl);
400 setverdict(pass);
401 }
402
403 /* test CRCX with unsupported packetization interval */
404 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
405 var template MgcpCommand cmd;
406 var MgcpResponse resp;
407 var template MgcpResponse rtmpl := tr_MgcpResp_Err("532");
408
409 f_init();
410
411 cmd := ts_CRCX(get_next_trans_id(), "2@mgw", "recvonly", '1234'H);
412 cmd.params := {
413 t_MgcpParConnMode("recvonly"),
414 ts_MgcpParCallId('1234'H),
415 t_MgcpParLocConnOpt("p:111")
416 }
417 resp := mgcp_transceive_mgw(cmd, rtmpl);
418 setverdict(pass);
419 }
420
421 /* test CRCX with illegal double presence of local connection option */
422 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
423 var template MgcpCommand cmd;
424 var MgcpResponse resp;
425 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
426
427 f_init();
428
429 cmd := ts_CRCX(get_next_trans_id(), "2@mgw", "recvonly", '1234'H);
430 cmd.params := {
431 t_MgcpParConnMode("recvonly"),
432 ts_MgcpParCallId('1234'H),
433 t_MgcpParLocConnOpt("p:20, a:AMR, p:20")
434 }
435 resp := mgcp_transceive_mgw(cmd, rtmpl);
436 setverdict(pass);
437 }
438
439 /* test valid CRCX with valid SDP */
440 testcase TC_crcx_sdp() runs on dummy_CT {
441 var template MgcpCommand cmd;
442 var MgcpResponse resp;
443 var template MgcpResponse rtmpl := {
444 line := {
445 code := "200",
446 string := "OK"
447 },
448 params:= ?,
449 sdp := ?
450 };
451
452 f_init();
453
454 cmd := ts_CRCX(get_next_trans_id(), "2@mgw", "sendrecv", '1234'H);
455 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
456 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
457 valueof(ts_SDP_ptime(20)) });
458 resp := mgcp_transceive_mgw(cmd, rtmpl);
459 setverdict(pass);
460 }
461
462 /* TODO: various SDP related bits */
463
464
465 /* TODO: CRCX with X-Osmux */
466 /* TODO: double CRCX without force_realloc */
467
468 /* TODO: MDCX (various) */
469
470 /* TODO: MDCX without CRCX first */
471 testcase TC_mdcx_without_crcx() runs on dummy_CT {
472 var template MgcpCommand cmd;
473 var MgcpResponse resp;
474 var template MgcpResponse rtmpl := {
475 line := {
476 /* TODO: accept/enforce better error? */
477 code := "400",
478 string := ?
479 },
480 params:= { },
481 sdp := omit
482 };
483
484 f_init();
485
486 cmd := ts_MDCX(get_next_trans_id(), "3@mgw", "sendrecv", '31234'H);
487 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
488 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
489 valueof(ts_SDP_ptime(20)) });
490 resp := mgcp_transceive_mgw(cmd, rtmpl);
491 setverdict(pass);
492 }
493
494 /* DLCX without CRCX first */
495 testcase TC_dlcx_without_crcx() runs on dummy_CT {
496 var template MgcpCommand cmd;
497 var MgcpResponse resp;
498 var template MgcpResponse rtmpl := {
499 line := {
500 /* TODO: accept/enforce better error? */
501 code := "400",
502 string := ?
503 },
504 params:= { },
505 sdp := omit
506 };
507
508 f_init();
509
510 cmd := ts_DLCX(get_next_trans_id(), "4@mgw", '41234'H);
511 resp := mgcp_transceive_mgw(cmd, rtmpl);
512 setverdict(pass);
513 }
514
515 /* TODO: DLCX of valid endpoint but invalid call-id */
516 /* TODO: Double-DLCX (retransmission) */
517 /* TODO: Double-DLCX (no retransmission) */
518
519
520
521 /* TODO: AUEP (various) */
522 /* TODO: RSIP (various) */
523 /* TODO: RQNT (various) */
524 /* TODO: EPCF (various) */
525 /* TODO: AUCX (various) */
526 /* TODO: invalid verb (various) */
527
Harald Welte00a067f2017-09-13 23:27:17 +0200528 control {
529 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +0800530 execute(TC_crcx());
Harald Weltee636afd2017-09-17 16:24:09 +0800531 execute(TC_crcx_unsupp_mode());
532 execute(TC_crcx_early_bidir_mode());
533 execute(TC_crcx_unsupp_param());
534 execute(TC_crcx_missing_callid());
535 execute(TC_crcx_missing_mode());
536 execute(TC_crcx_unsupp_packet_intv());
537 execute(TC_crcx_illegal_double_lco());
538 execute(TC_crcx_sdp());
539 execute(TC_mdcx_without_crcx());
540 execute(TC_dlcx_without_crcx());
Harald Welte00a067f2017-09-13 23:27:17 +0200541 }
542}