blob: 467ebfce474502139bbdf269a214e98ba2bd61b9 [file] [log] [blame]
Harald Welte00a067f2017-09-13 23:27:17 +02001module MGCP_Test {
Harald Weltef07c2862017-11-18 17:16:24 +01002 import from Osmocom_Types all;
Harald Welte00a067f2017-09-13 23:27:17 +02003 import from MGCP_Types all;
Harald Welte4029e8c2017-11-23 22:00:42 +01004 import from MGCP_Templates all;
Harald Welte3c6ebb92017-09-16 00:56:57 +08005 import from SDP_Types all;
6 import from MGCP_CodecPort all;
7 import from MGCP_CodecPort_CtrlFunct all;
Harald Weltef07c2862017-11-18 17:16:24 +01008 import from RTP_CodecPort all;
9 import from RTP_CodecPort_CtrlFunct all;
Harald Weltebb7523b2018-03-29 08:52:01 +020010 import from RTP_Emulation all;
Harald Welte3c6ebb92017-09-16 00:56:57 +080011 import from IPL4asp_Types all;
Philipp Maier6137c322019-02-20 16:13:41 +010012 import from General_Types all;
13 import from Native_Functions all;
14 import from IPCP_Types all;
15 import from IP_Types all;
16 import from Osmocom_VTY_Functions all;
17 import from TELNETasp_PortType all;
Harald Welte00a067f2017-09-13 23:27:17 +020018
Philipp Maierbb7a01c2018-02-01 12:32:57 +010019 const charstring c_mgw_domain := "mgw";
20 const charstring c_mgw_ep_rtpbridge := "rtpbridge/";
21
Harald Welte21ba5572017-09-19 17:55:05 +080022 /* any variables declared in the component will be available to
23 * all functions that 'run on' the named component, similar to
24 * class members in C++ */
Harald Welte00a067f2017-09-13 23:27:17 +020025 type component dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080026 port MGCP_CODEC_PT MGCP;
27 var boolean initialized := false;
Harald Welte55015362017-11-18 16:02:42 +010028 var ConnectionId g_mgcp_conn_id := -1;
Harald Weltee1e18c52017-09-17 16:23:07 +080029 var integer g_trans_id;
Harald Weltef07c2862017-11-18 17:16:24 +010030
Philipp Maier2321ef92018-06-27 17:52:04 +020031 var RTP_Emulation_CT vc_RTPEM[3];
32 port RTPEM_CTRL_PT RTPEM[3];
Philipp Maier6137c322019-02-20 16:13:41 +010033
34 port TELNETasp_PT MGWVTY;
Harald Welte00a067f2017-09-13 23:27:17 +020035 };
36
Harald Weltee1e18c52017-09-17 16:23:07 +080037 function get_next_trans_id() runs on dummy_CT return MgcpTransId {
38 var MgcpTransId tid := int2str(g_trans_id);
39 g_trans_id := g_trans_id + 1;
40 return tid;
41 }
42
Harald Welte21ba5572017-09-19 17:55:05 +080043 /* all parameters declared here can be modified / overridden by
44 * the config file in the [MODULE_PARAMETERS] section. If no
45 * config file is used or the file doesn't specify them, the
46 * default values assigned below are used */
Harald Welte3c6ebb92017-09-16 00:56:57 +080047 modulepar {
48 PortNumber mp_local_udp_port := 2727;
49 charstring mp_local_ip := "127.0.0.1";
50 PortNumber mp_remote_udp_port := 2427;
51 charstring mp_remote_ip := "127.0.0.1";
Harald Weltef07c2862017-11-18 17:16:24 +010052 PortNumber mp_local_rtp_port_base := 10000;
Harald Welte3c6ebb92017-09-16 00:56:57 +080053 }
54
Philipp Maier6137c322019-02-20 16:13:41 +010055 private function f_init_vty() runs on dummy_CT {
56 map(self:MGWVTY, system:MGWVTY);
57 f_vty_set_prompts(MGWVTY);
58 f_vty_transceive(MGWVTY, "enable");
59 }
60
Harald Weltebb7523b2018-03-29 08:52:01 +020061 private function f_rtpem_init(inout RTP_Emulation_CT comp_ref, integer i)
62 runs on dummy_CT {
63 comp_ref := RTP_Emulation_CT.create("RTPEM" & int2str(i));
64 map(comp_ref:RTP, system:RTP);
65 map(comp_ref:RTCP, system:RTCP);
66 comp_ref.start(RTP_Emulation.f_main());
67 }
68
Harald Welte21ba5572017-09-19 17:55:05 +080069 /* initialization function, called by each test case at the
70 * beginning, but 'initialized' variable ensures its body is
71 * only executed once */
Harald Welteedc45c12017-11-18 19:15:05 +010072 private function f_init(template MgcpEndpoint ep := omit) runs on dummy_CT {
Harald Welte3c6ebb92017-09-16 00:56:57 +080073 var Result res;
Harald Weltef07c2862017-11-18 17:16:24 +010074 var uint32_t ssrc;
Philipp Maier28bb8292018-07-20 17:09:17 +020075
Harald Welteedc45c12017-11-18 19:15:05 +010076 if (initialized == false) {
77 initialized := true;
78
79 /* some random number for the initial transaction id */
80 g_trans_id := float2int(rnd()*65535.0);
81 map(self:MGCP, system:MGCP_CODEC_PT);
82 /* connect the MGCP test port using the given
83 * source/destionation ip/port and store the connection id in g_mgcp_conn_id
84 * */
85 res := MGCP_CodecPort_CtrlFunct.f_IPL4_connect(MGCP, mp_remote_ip, mp_remote_udp_port, mp_local_ip, mp_local_udp_port, 0, { udp := {} });
Harald Welte9220f632018-05-23 20:27:02 +020086 if (not ispresent(res.connId)) {
87 setverdict(fail, "Could not connect MGCP, check your configuration");
Daniel Willmannafce8662018-07-06 23:11:32 +020088 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +020089 }
Harald Welteedc45c12017-11-18 19:15:05 +010090 g_mgcp_conn_id := res.connId;
91
Harald Weltebb7523b2018-03-29 08:52:01 +020092 for (var integer i := 0; i < sizeof(vc_RTPEM); i := i+1) {
93 f_rtpem_init(vc_RTPEM[i], i);
94 connect(vc_RTPEM[i]:CTRL, self:RTPEM[i]);
95 }
Harald Welte3c6ebb92017-09-16 00:56:57 +080096 }
Harald Welte3c6ebb92017-09-16 00:56:57 +080097
Harald Welteedc45c12017-11-18 19:15:05 +010098 if (isvalue(ep)) {
99 /* do a DLCX on all connections of the EP */
100 f_dlcx_ignore(valueof(ep));
101 }
Philipp Maier6137c322019-02-20 16:13:41 +0100102
103 f_init_vty();
Harald Welte3c6ebb92017-09-16 00:56:57 +0800104 }
105
Harald Welte00a067f2017-09-13 23:27:17 +0200106 testcase TC_selftest() runs on dummy_CT {
107 const charstring c_auep := "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100108 const charstring c_mdcx3 := "MDCX 18983215 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n";
Harald Welte00a067f2017-09-13 23:27:17 +0200109 const charstring c_mdcx3_ret := "200 18983215 OK\r\n" &
110 "I: 1\n" &
111 "\n" &
112 "v=0\r\n" &
113 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
114 "s=-\r\n" &
115 "c=IN IP4 0.0.0.0\r\n" &
116 "t=0 0\r\n" &
117 "m=audio 0 RTP/AVP 126\r\n" &
118 "a=rtpmap:126 AMR/8000\r\n" &
119 "a=ptime:20\r\n";
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100120 const charstring c_mdcx4 := "MDCX 18983216 " & c_mgw_ep_rtpbridge & "1@" & c_mgw_domain & " MGCP 1.0\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200121 "M: sendrecv\r" &
122 "C: 2\r\n" &
123 "I: 1\r\n" &
124 "L: p:20, a:AMR, nt:IN\r\n" &
125 "\n" &
126 "v=0\r\n" &
127 "o=- 1 23 IN IP4 0.0.0.0\r\n" &
Harald Welte2871d0b2017-09-14 22:42:12 +0800128 "s=-\r\n" &
Harald Welte00a067f2017-09-13 23:27:17 +0200129 "c=IN IP4 0.0.0.0\r\n" &
130 "t=0 0\r\n" &
131 "m=audio 4441 RTP/AVP 99\r\n" &
132 "a=rtpmap:99 AMR/8000\r\n" &
133 "a=ptime:40\r\n";
Harald Welte3c6ebb92017-09-16 00:56:57 +0800134 const charstring c_crcx510_ret := "510 23 FAIL\r\n"
Harald Welte00a067f2017-09-13 23:27:17 +0200135
136 log(c_auep);
137 log(dec_MgcpCommand(c_auep));
138
139 log(c_mdcx3);
140 log(dec_MgcpCommand(c_mdcx3));
141
142 log(c_mdcx3_ret);
143 log(dec_MgcpResponse(c_mdcx3_ret));
144
145 log(c_mdcx4);
146 log(dec_MgcpCommand(c_mdcx4));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800147
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100148 log(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H));
149 log(enc_MgcpCommand(valueof(ts_CRCX("23", c_mgw_ep_rtpbridge & "42@" & c_mgw_domain, "sendrecv", '1234'H))));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800150
151 log(c_crcx510_ret);
152 log(dec_MgcpResponse(c_crcx510_ret));
153 log(dec_MgcpMessage(c_crcx510_ret));
154 }
155
Harald Weltee636afd2017-09-17 16:24:09 +0800156 /* CRCX test ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100157 * x without mandatory CallId
Harald Weltee636afd2017-09-17 16:24:09 +0800158 * - with forbidden parameters (e.g. Capabilities, PackageList, ...
159 * - CRCX with remote session description and without
160 *
161 * general ideas:
Harald Weltee0b331f2017-11-18 20:34:33 +0100162 * x packetization != 20ms
163 * x invalid mode
Harald Weltee636afd2017-09-17 16:24:09 +0800164 * x unsupported mode (517)
165 * x bidirectional mode before RemoteConnDesc: 527
166 * - invalid codec
Harald Weltee0b331f2017-11-18 20:34:33 +0100167 * x retransmission of same transaction
Harald Weltee636afd2017-09-17 16:24:09 +0800168 * - unsupported LocalConnectionOptions ("b", "a", "e", "gc", "s", "r", "k", ..)
169 */
170
Harald Welte21ba5572017-09-19 17:55:05 +0800171 /* build a receive template for receiving a MGCP message. You
172 * pass the MGCP response template in, and it will generate an
173 * MGCP_RecvFrom template that can match the primitives arriving on the
174 * MGCP_CodecPort */
Harald Weltee636afd2017-09-17 16:24:09 +0800175 function tr_MGCP_RecvFrom_R(template MgcpResponse resp) runs on dummy_CT return template MGCP_RecvFrom {
176 var template MGCP_RecvFrom mrf := {
Harald Welte55015362017-11-18 16:02:42 +0100177 connId := g_mgcp_conn_id,
Harald Weltee636afd2017-09-17 16:24:09 +0800178 remName := mp_remote_ip,
179 remPort := mp_remote_udp_port,
180 locName := mp_local_ip,
181 locPort := mp_local_udp_port,
182 msg := { response := resp }
183 }
184 return mrf;
185 }
186
187 /* Send a MGCP request + receive a (matching!) response */
188 function mgcp_transceive_mgw(template MgcpCommand cmd, template MgcpResponse resp := ?) runs on dummy_CT return MgcpResponse {
189 var MgcpMessage msg := { command := valueof(cmd) };
190 resp.line.trans_id := cmd.line.trans_id;
191 var template MGCP_RecvFrom mrt := tr_MGCP_RecvFrom_R(resp);
Harald Welte3c6ebb92017-09-16 00:56:57 +0800192 var MGCP_RecvFrom mrf;
193 timer T := 5.0;
194
Harald Welte55015362017-11-18 16:02:42 +0100195 MGCP.send(t_MGCP_Send(g_mgcp_conn_id, msg));
Harald Welte3c6ebb92017-09-16 00:56:57 +0800196 T.start;
197 alt {
Harald Weltee636afd2017-09-17 16:24:09 +0800198 [] MGCP.receive(mrt) -> value mrf { }
Daniel Willmannafce8662018-07-06 23:11:32 +0200199 [] MGCP.receive(tr_MGCP_RecvFrom_R(?)) {
200 setverdict(fail, "Response didn't match template");
201 mtc.stop;
202 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800203 [] MGCP.receive { repeat; }
Daniel Willmannafce8662018-07-06 23:11:32 +0200204 [] T.timeout {
205 setverdict(fail, "Timeout waiting for response to ", cmd);
206 mtc.stop;
207 }
Harald Welte3c6ebb92017-09-16 00:56:57 +0800208 }
209 T.stop;
Harald Weltee636afd2017-09-17 16:24:09 +0800210
211 if (isbound(mrf) and isbound(mrf.msg) and ischosen(mrf.msg.response)) {
212 return mrf.msg.response;
213 } else {
214 var MgcpResponse r := { line := { code := "999", trans_id := valueof(cmd.line.trans_id) } };
215 return r;
216 }
Harald Welte00a067f2017-09-13 23:27:17 +0200217 }
218
Harald Welteba62c8c2017-11-18 18:26:49 +0100219 function extract_conn_id(MgcpResponse resp) return MgcpConnectionId {
220 var integer i;
221 for (i := 0; i < lengthof(resp.params); i := i + 1) {
222 var MgcpParameter par := resp.params[i];
223 if (par.code == "I") {
224 return str2hex(par.val);
225 }
226 }
Daniel Willmannafce8662018-07-06 23:11:32 +0200227 setverdict(fail, "Could not find conn id for MgcpReponse");
228 mtc.stop;
Harald Welteba62c8c2017-11-18 18:26:49 +0100229 return '00000000'H;
230 }
231
Harald Welte10889c12017-11-18 19:40:31 +0100232 function f_dlcx(MgcpEndpoint ep, template MgcpResponseCode ret_code, template charstring ret_val,
233 template MgcpCallId call_id := omit,
234 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welteba62c8c2017-11-18 18:26:49 +0100235 var template MgcpCommand cmd;
236 var MgcpResponse resp;
237 var template MgcpResponse rtmpl := {
238 line := {
Harald Welte10889c12017-11-18 19:40:31 +0100239 code := ret_code,
240 string := ret_val
Harald Welteba62c8c2017-11-18 18:26:49 +0100241 },
242 params := *,
243 sdp := *
244 };
Harald Weltec40e0c32017-11-18 19:08:22 +0100245 cmd := ts_DLCX(get_next_trans_id(), ep, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100246 resp := mgcp_transceive_mgw(cmd, rtmpl);
247 }
248
Harald Welte10889c12017-11-18 19:40:31 +0100249 /* Send DLCX and expect OK response */
250 function f_dlcx_ok(MgcpEndpoint ep, template MgcpCallId call_id := omit,
251 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Weltef91edf32017-12-28 14:16:21 +0100252 f_dlcx(ep, ("200","250"), "OK", call_id, conn_id);
Harald Welte10889c12017-11-18 19:40:31 +0100253 }
254
Harald Welteba62c8c2017-11-18 18:26:49 +0100255 /* Send DLCX and accept any response */
Harald Weltec40e0c32017-11-18 19:08:22 +0100256 function f_dlcx_ignore(MgcpEndpoint ep, template MgcpCallId call_id := omit,
Harald Welteba62c8c2017-11-18 18:26:49 +0100257 template MgcpConnectionId conn_id := omit) runs on dummy_CT {
Harald Welte10889c12017-11-18 19:40:31 +0100258 f_dlcx(ep, ?, *, call_id, conn_id);
Harald Welteba62c8c2017-11-18 18:26:49 +0100259 }
260
Harald Weltebb7523b2018-03-29 08:52:01 +0200261 type record HostPort {
262 charstring hostname,
263 integer portnr optional
264 }
265 type record RtpFlowData {
266 HostPort em, /* emulation side */
267 HostPort mgw, /* mgw side */
268 uint7_t pt,
269 charstring codec,
Philipp Maier28bb8292018-07-20 17:09:17 +0200270 MgcpConnectionId mgcp_conn_id optional,
Philipp Maierc8c0b402019-03-07 10:48:45 +0100271 RtpemConfig rtp_cfg optional,
272 charstring fmtp optional
Harald Weltebb7523b2018-03-29 08:52:01 +0200273 }
274
Philipp Maier2321ef92018-06-27 17:52:04 +0200275 /* Create an RTP flow (bidirectional, or receive-only) */
276 function f_flow_create(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow,
Harald Weltebb7523b2018-03-29 08:52:01 +0200277 boolean one_phase := true)
278 runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +0200279 var template MgcpCommand cmd;
280 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100281 var SDP_attribute_list attributes;
282
283 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
284 if (isvalue(flow.fmtp)) {
285 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
286 }
Harald Weltebb7523b2018-03-29 08:52:01 +0200287
288 /* bind local RTP emulation socket */
289 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
290
Philipp Maier28bb8292018-07-20 17:09:17 +0200291 /* configure rtp-emulation */
292 if (ispresent(flow.rtp_cfg)) {
293 f_rtpem_configure(pt, flow.rtp_cfg);
294 } else {
295 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
296 rtp_cfg.tx_payload_type := flow.pt
297 f_rtpem_configure(pt, rtp_cfg);
298 }
299
Harald Weltebb7523b2018-03-29 08:52:01 +0200300 if (one_phase) {
Philipp Maier2321ef92018-06-27 17:52:04 +0200301 /* Connect flow to MGW using a CRCX that also contains an SDP
302 * part that tells the MGW where we are listening for RTP streams
303 * that come from the MGW. We get a fully working connection in
304 * one go. */
305
306 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Welteedc88742018-03-29 18:01:16 +0200307 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100308 flow.em.portnr, { int2str(flow.pt) }, attributes);
309
Harald Weltebb7523b2018-03-29 08:52:01 +0200310 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
311 flow.mgcp_conn_id := extract_conn_id(resp);
312 /* extract port number from response */
313 flow.mgw.portnr :=
314 resp.sdp.media_list[0].media_field.ports.port_number;
315 } else {
Philipp Maier2321ef92018-06-27 17:52:04 +0200316 /* Create a half-open connection only. We do not tell the MGW
317 * where it can send RTP streams to us. This means this
318 * connection will only be able to receive but can not send
319 * data back to us. In order to turn the connection in a fully
320 * bi-directional one, a separate MDCX is needed. */
321
322 cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +0200323 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
324 flow.mgcp_conn_id := extract_conn_id(resp);
325 /* extract MGW-side port number from response */
326 flow.mgw.portnr :=
327 resp.sdp.media_list[0].media_field.ports.port_number;
Harald Weltebb7523b2018-03-29 08:52:01 +0200328 }
329 /* finally, connect the emulation-side RTP socket to the MGW */
330 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
331 }
332
Philipp Maier2321ef92018-06-27 17:52:04 +0200333 /* Modify an existing RTP flow */
334 function f_flow_modify(RTPEM_CTRL_PT pt, MgcpEndpoint ep, MgcpCallId call_id, charstring mode, inout RtpFlowData flow)
335 runs on dummy_CT {
336 var template MgcpCommand cmd;
337 var MgcpResponse resp;
Philipp Maierc8c0b402019-03-07 10:48:45 +0100338 var SDP_attribute_list attributes;
339
340 attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
341 if (isvalue(flow.fmtp)) {
342 attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
343 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200344
345 /* rebind local RTP emulation socket to the new address */
346 f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
347
Philipp Maier28bb8292018-07-20 17:09:17 +0200348 /* reconfigure rtp-emulation */
349 if (ispresent(flow.rtp_cfg)) {
350 f_rtpem_configure(pt, flow.rtp_cfg);
351 } else {
352 var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
353 rtp_cfg.tx_payload_type := flow.pt
354 f_rtpem_configure(pt, rtp_cfg);
355 }
356
Philipp Maier2321ef92018-06-27 17:52:04 +0200357 /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
358 cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
359 cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
Philipp Maierc8c0b402019-03-07 10:48:45 +0100360 flow.em.portnr, { int2str(flow.pt) }, attributes);
Philipp Maier2321ef92018-06-27 17:52:04 +0200361 resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
362
363 /* extract MGW-side port number from response. (usually this
364 * will not change, but thats is up to the MGW) */
365 flow.mgw.portnr :=
366 resp.sdp.media_list[0].media_field.ports.port_number;
367
368 /* reconnect the emulation-side RTP socket to the MGW */
369 f_rtpem_connect(pt, flow.mgw.hostname, flow.mgw.portnr);
370 }
371
372 /* Delete an existing RTP flow */
373 function f_flow_delete(RTPEM_CTRL_PT pt, template MgcpEndpoint ep := omit, template MgcpCallId call_id := omit)
374 runs on dummy_CT {
375 var template MgcpCommand cmd;
376 var MgcpResponse resp;
377
378 /* Switch off RTP flow */
379 f_rtpem_mode(pt, RTPEM_MODE_NONE);
380
381 /* Delete connection on MGW (if needed) */
382 if (isvalue(call_id) and isvalue(ep)) {
383 f_sleep(0.1);
384 f_dlcx_ok(valueof(ep), call_id);
385 }
386 }
387
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100388 function f_crcx(charstring ep_prefix) runs on dummy_CT {
389 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800390 var template MgcpCommand cmd;
391 var MgcpResponse resp;
Harald Welteba62c8c2017-11-18 18:26:49 +0100392 var MgcpCallId call_id := '1234'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800393
Harald Welteedc45c12017-11-18 19:15:05 +0100394 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800395
Harald Welteba62c8c2017-11-18 18:26:49 +0100396 /* create the connection on the MGW */
397 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100398 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welteba62c8c2017-11-18 18:26:49 +0100399 extract_conn_id(resp);
400
401 /* clean-up */
402 f_dlcx_ok(ep, call_id);
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100403 }
Harald Welteba62c8c2017-11-18 18:26:49 +0100404
Philipp Maier45635f42018-06-05 17:28:02 +0200405 function f_crcx_no_lco(charstring ep_prefix) runs on dummy_CT {
406 var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain;
407 var template MgcpCommand cmd;
408 var MgcpResponse resp;
409 var MgcpCallId call_id := '1234'H;
410
411 f_init(ep);
412
413 /* create the connection on the MGW */
414 cmd := ts_CRCX_no_lco(get_next_trans_id(), ep, "recvonly", call_id);
415 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
416 extract_conn_id(resp);
417
418 /* clean-up */
419 f_dlcx_ok(ep, call_id);
420
421 /* See also OS#2658: Even when we omit the LCO information, we
422 expect the MGW to pick a sane payload type for us. This
423 payload type should be visible in the SDP of the response. */
Philipp Maier76a8d012018-06-21 10:03:13 +0200424 if (resp.sdp.media_list[0].media_field.fmts[0] != "0") {
Philipp Maier45635f42018-06-05 17:28:02 +0200425 setverdict(fail, "SDP contains unexpected codec");
Daniel Willmannafce8662018-07-06 23:11:32 +0200426 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200427 }
428
429 /* See also OS#2658: We also expect the MGW to assign a port
430 number to us. */
431 if (isbound(resp.sdp.media_list[0].media_field.ports.port_number) == false) {
432 setverdict(fail, "SDP does not contain a port number");
Daniel Willmannafce8662018-07-06 23:11:32 +0200433 mtc.stop;
Philipp Maier45635f42018-06-05 17:28:02 +0200434 }
435 }
436
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100437 /* test valid CRCX without SDP */
438 testcase TC_crcx() runs on dummy_CT {
439 f_crcx(c_mgw_ep_rtpbridge);
440 setverdict(pass);
441 }
442
Philipp Maier45635f42018-06-05 17:28:02 +0200443 /* test valid CRCX without SDP and LCO */
444 testcase TC_crcx_no_lco() runs on dummy_CT {
445 f_crcx_no_lco(c_mgw_ep_rtpbridge);
446 setverdict(pass);
447 }
448
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100449 /* test valid CRCX without SDP (older method without endpoint prefix) */
450 testcase TC_crcx_noprefix() runs on dummy_CT {
451 f_crcx("");
Harald Weltee636afd2017-09-17 16:24:09 +0800452 setverdict(pass);
453 }
454
455 /* test CRCX with unsupported mode, expect 517 */
456 testcase TC_crcx_unsupp_mode() runs on dummy_CT {
457 var template MgcpCommand cmd;
458 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100459 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100460 var MgcpCallId call_id := '1233'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800461 var template MgcpResponse rtmpl := tr_MgcpResp_Err("517");
462
Harald Welteedc45c12017-11-18 19:15:05 +0100463 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800464
Harald Welteba62c8c2017-11-18 18:26:49 +0100465 cmd := ts_CRCX(get_next_trans_id(), ep, "netwtest", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800466 resp := mgcp_transceive_mgw(cmd, rtmpl);
467 setverdict(pass);
468 }
469
Harald Welte21ba5572017-09-19 17:55:05 +0800470 /* test CRCX with early bi-directional mode, expect 527 as
471 * bi-diretional media can only be established once both local and
472 * remote side are specified, see MGCP RFC */
Harald Weltee636afd2017-09-17 16:24:09 +0800473 testcase TC_crcx_early_bidir_mode() runs on dummy_CT {
474 var template MgcpCommand cmd;
475 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100476 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100477 var MgcpCallId call_id := '1232'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800478 var template MgcpResponse rtmpl := tr_MgcpResp_Err("527");
479
Harald Welteedc45c12017-11-18 19:15:05 +0100480 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800481
Harald Welteba62c8c2017-11-18 18:26:49 +0100482 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800483 resp := mgcp_transceive_mgw(cmd, rtmpl);
484 setverdict(pass);
485 }
486
487 /* test CRCX with unsupported Parameters */
488 testcase TC_crcx_unsupp_param() runs on dummy_CT {
489 var template MgcpCommand cmd;
490 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100491 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100492 var MgcpCallId call_id := '1231'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800493 var template MgcpResponse rtmpl := tr_MgcpResp_Err("539");
494
Harald Welteedc45c12017-11-18 19:15:05 +0100495 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800496
Harald Welteba62c8c2017-11-18 18:26:49 +0100497 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100498 /* osmo-bsc_mgcp/mgw doesn't implement notifications */
499 f_mgcp_par_append(cmd.params, MgcpParameter:{ "N", "foobar" });
500
Harald Weltee636afd2017-09-17 16:24:09 +0800501 resp := mgcp_transceive_mgw(cmd, rtmpl);
502 setverdict(pass);
503 }
504
505 /* test CRCX with missing CallId */
506 testcase TC_crcx_missing_callid() runs on dummy_CT {
507 var template MgcpCommand cmd;
508 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100509 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Weltef91edf32017-12-28 14:16:21 +0100510 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","516"));
Harald Weltee636afd2017-09-17 16:24:09 +0800511
Harald Welteedc45c12017-11-18 19:15:05 +0100512 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800513
Harald Welteba62c8c2017-11-18 18:26:49 +0100514 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", '1230'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800515 cmd.params := {
516 t_MgcpParConnMode("recvonly"),
517 t_MgcpParLocConnOpt("p:20")
518 }
519 resp := mgcp_transceive_mgw(cmd, rtmpl);
520 setverdict(pass);
Harald Welteba62c8c2017-11-18 18:26:49 +0100521
Harald Weltee636afd2017-09-17 16:24:09 +0800522 }
523
524 /* test CRCX with missing Mode */
525 testcase TC_crcx_missing_mode() runs on dummy_CT {
526 var template MgcpCommand cmd;
527 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100528 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100529 var MgcpCallId call_id := '1229'H;
Harald Weltef91edf32017-12-28 14:16:21 +0100530 var template MgcpResponse rtmpl := tr_MgcpResp_Err(("400","517"));
Harald Weltee636afd2017-09-17 16:24:09 +0800531
Harald Welteedc45c12017-11-18 19:15:05 +0100532 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800533
Harald Welteba62c8c2017-11-18 18:26:49 +0100534 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800535 cmd.params := {
Harald Welteba62c8c2017-11-18 18:26:49 +0100536 ts_MgcpParCallId(call_id),
Harald Weltee636afd2017-09-17 16:24:09 +0800537 t_MgcpParLocConnOpt("p:20")
538 }
539 resp := mgcp_transceive_mgw(cmd, rtmpl);
540 setverdict(pass);
541 }
542
543 /* test CRCX with unsupported packetization interval */
544 testcase TC_crcx_unsupp_packet_intv() runs on dummy_CT {
545 var template MgcpCommand cmd;
546 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100547 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100548 var MgcpCallId call_id := '1228'H;
Harald Welte0d198612017-11-18 19:58:31 +0100549 var template MgcpResponse rtmpl := tr_MgcpResp_Err("535");
Harald Weltee636afd2017-09-17 16:24:09 +0800550
Harald Welteedc45c12017-11-18 19:15:05 +0100551 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800552
Harald Welteba62c8c2017-11-18 18:26:49 +0100553 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100554 cmd.params[2] := t_MgcpParLocConnOpt("p:111");
Harald Weltee636afd2017-09-17 16:24:09 +0800555 resp := mgcp_transceive_mgw(cmd, rtmpl);
556 setverdict(pass);
557 }
558
559 /* test CRCX with illegal double presence of local connection option */
560 testcase TC_crcx_illegal_double_lco() runs on dummy_CT {
561 var template MgcpCommand cmd;
562 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100563 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100564 var MgcpCallId call_id := '1227'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800565 var template MgcpResponse rtmpl := tr_MgcpResp_Err("524");
566
Harald Welteedc45c12017-11-18 19:15:05 +0100567 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800568
Harald Welteba62c8c2017-11-18 18:26:49 +0100569 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Weltea01e38d2017-11-18 18:40:01 +0100570 /* p:20 is permitted only once and not twice! */
571 cmd.params[2] := t_MgcpParLocConnOpt("p:20, a:AMR, p:20");
Harald Weltee636afd2017-09-17 16:24:09 +0800572 resp := mgcp_transceive_mgw(cmd, rtmpl);
573 setverdict(pass);
574 }
575
576 /* test valid CRCX with valid SDP */
577 testcase TC_crcx_sdp() runs on dummy_CT {
578 var template MgcpCommand cmd;
579 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100580 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100581 var MgcpCallId call_id := '1226'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800582
Harald Welteedc45c12017-11-18 19:15:05 +0100583 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800584
Harald Welteba62c8c2017-11-18 18:26:49 +0100585 cmd := ts_CRCX(get_next_trans_id(), ep, "sendrecv", call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800586 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
587 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
588 valueof(ts_SDP_ptime(20)) });
Harald Welte9988d282017-11-18 19:22:00 +0100589 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Philipp Maierdffa6a42018-02-02 11:55:44 +0100590
591 /* clean-up */
592 f_dlcx_ok(ep, call_id);
593
Harald Weltee636afd2017-09-17 16:24:09 +0800594 setverdict(pass);
595 }
596
Philipp Maier5e06cee2018-02-01 18:28:08 +0100597 /* test valid wildcarded CRCX */
598 testcase TC_crcx_wildcarded() runs on dummy_CT {
599 var template MgcpCommand cmd;
600 var MgcpResponse resp;
601 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
602 var MgcpCallId call_id := '1234'H;
603 var MgcpEndpoint ep_assigned;
604 f_init();
605
606 /* create the connection on the MGW */
607 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
608 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
609 extract_conn_id(resp);
610
611 /* extract endpoint name we got assigned by the MGW */
612 var MgcpMessage resp_msg := {
613 response := resp
614 }
615 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) {
616 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200617 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100618 }
619
620 /* clean-up */
621 f_dlcx_ok(ep_assigned, call_id);
622
623 setverdict(pass);
624 }
625
626 /* test valid wildcarded CRCX */
627 testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT {
628 const integer n_endpoints := 32;
629 var integer i;
630 var template MgcpCommand cmd;
631 var MgcpResponse resp;
632 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
633 var MgcpCallId call_id := '1234'H;
634 var MgcpEndpoint ep_assigned[n_endpoints];
635 f_init();
636
637 /* Exhaust all endpoint resources on the virtual trunk */
638 for (i := 0; i < n_endpoints; i := i+1) {
639 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
640 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
641
642 /* Make sure we got a connection id */
643 extract_conn_id(resp);
644
645 var MgcpMessage resp_msg := {
646 response := resp
647 }
648 if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) {
649 setverdict(fail, "No SpecificEndpointName in MGCP response", resp);
Daniel Willmannafce8662018-07-06 23:11:32 +0200650 mtc.stop;
Philipp Maier5e06cee2018-02-01 18:28:08 +0100651 }
652 }
653
654 /* Try to allocate one more endpoint, which should fail */
655 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
656 var template MgcpResponse rtmpl := tr_MgcpResp_Err("403");
657 resp := mgcp_transceive_mgw(cmd, rtmpl);
658 setverdict(pass);
659
660 /* clean-up */
661 for (i := 0; i < n_endpoints; i := i+1) {
662 f_dlcx_ok(ep_assigned[i], call_id);
663 }
664 setverdict(pass);
665 }
666
Harald Weltee636afd2017-09-17 16:24:09 +0800667 /* TODO: various SDP related bits */
668
669
670 /* TODO: CRCX with X-Osmux */
671 /* TODO: double CRCX without force_realloc */
672
673 /* TODO: MDCX (various) */
674
675 /* TODO: MDCX without CRCX first */
676 testcase TC_mdcx_without_crcx() runs on dummy_CT {
677 var template MgcpCommand cmd;
678 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100679 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "3@" & c_mgw_domain;
Harald Welteba62c8c2017-11-18 18:26:49 +0100680 var MgcpCallId call_id := '1225'H;
Harald Weltee636afd2017-09-17 16:24:09 +0800681 var template MgcpResponse rtmpl := {
682 line := {
683 /* TODO: accept/enforce better error? */
684 code := "400",
685 string := ?
686 },
687 params:= { },
688 sdp := omit
689 };
690
Harald Welteedc45c12017-11-18 19:15:05 +0100691 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800692
Harald Welte2bcfd3a2017-11-18 22:14:35 +0100693 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
Harald Weltee636afd2017-09-17 16:24:09 +0800694 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
695 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
696 valueof(ts_SDP_ptime(20)) });
697 resp := mgcp_transceive_mgw(cmd, rtmpl);
698 setverdict(pass);
699 }
700
701 /* DLCX without CRCX first */
702 testcase TC_dlcx_without_crcx() runs on dummy_CT {
703 var template MgcpCommand cmd;
704 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100705 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
Harald Weltee636afd2017-09-17 16:24:09 +0800706 var template MgcpResponse rtmpl := {
707 line := {
Harald Weltef91edf32017-12-28 14:16:21 +0100708 code := ("400", "515"),
Harald Weltee636afd2017-09-17 16:24:09 +0800709 string := ?
710 },
711 params:= { },
712 sdp := omit
713 };
714
Harald Welteedc45c12017-11-18 19:15:05 +0100715 f_init(ep);
Harald Weltee636afd2017-09-17 16:24:09 +0800716
Harald Welteedc45c12017-11-18 19:15:05 +0100717 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
Harald Weltee636afd2017-09-17 16:24:09 +0800718 resp := mgcp_transceive_mgw(cmd, rtmpl);
719 setverdict(pass);
720 }
721
Philipp Maier8a3dc922018-02-02 14:55:12 +0100722 /* test valid wildcarded MDCX */
723 testcase TC_mdcx_wildcarded() runs on dummy_CT {
724 /* Note: A wildcarded MDCX is not allowed, so we expect the
725 * MGW to reject this request */
726 var template MgcpCommand cmd;
727 var MgcpResponse resp;
728 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
729 var MgcpCallId call_id := '1225'H;
730 var template MgcpResponse rtmpl := {
731 line := {
732 /* TODO: accept/enforce better error? */
733 code := "507",
734 string := ?
735 },
736 params:= { },
737 sdp := omit
738 };
739
740 f_init(ep);
741
742 cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, call_id);
743 cmd.sdp := ts_SDP("127.0.0.1", "127.0.0.2", "23", "42", 2344, { "98" },
744 { valueof(ts_SDP_rtpmap(98, "AMR/8000")),
745 valueof(ts_SDP_ptime(20)) });
746 resp := mgcp_transceive_mgw(cmd, rtmpl);
747 setverdict(pass);
748 }
749
750 /* test valid wildcarded DLCX */
751 testcase TC_dlcx_wildcarded() runs on dummy_CT {
752 /* Note: A wildcarded DLCX is specified, but our MGW does not
753 * support this feature so we expect the MGW to reject the
754 * request */
755 var template MgcpCommand cmd;
756 var MgcpResponse resp;
757 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain;
758 var template MgcpResponse rtmpl := {
759 line := {
760 code := "507",
761 string := ?
762 },
763 params:= { },
764 sdp := omit
765 };
766
767 f_init(ep);
768
769 cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H);
770 resp := mgcp_transceive_mgw(cmd, rtmpl);
771 setverdict(pass);
772 }
773
Harald Welte79181ff2017-11-18 19:26:11 +0100774 /* Test (valid) CRCX followed by (valid) DLCX containig EP+CallId+ConnId */
775 testcase TC_crcx_and_dlcx_ep_callid_connid() runs on dummy_CT {
776 var template MgcpCommand cmd;
777 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100778 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte79181ff2017-11-18 19:26:11 +0100779 var MgcpCallId call_id := '51234'H;
780
781 f_init(ep);
782
783 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
784 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
785
786 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
787
788 setverdict(pass);
789 }
790
Harald Welte646ecdb2017-12-28 03:21:57 +0100791 function f_crcx_and_dlcx_ep_callid_connid(MgcpEndpoint ep, MgcpCallId call_id) runs on dummy_CT {
792 var template MgcpCommand cmd;
793 var MgcpResponse resp;
794
795 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
796 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
797
798 f_dlcx_ok(ep, call_id, extract_conn_id(resp));
799
800 setverdict(pass);
801 }
802
803 testcase TC_crcx_dlcx_30ep() runs on dummy_CT {
804 var MgcpEndpoint ep;
805 var MgcpCallId call_id;
806 var integer ep_nr;
807
808 f_init();
809
810 for (ep_nr := 1; ep_nr < 30; ep_nr := ep_nr+1) {
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100811 ep := c_mgw_ep_rtpbridge & hex2str(int2hex(ep_nr, 2)) & "@" & c_mgw_domain;
Harald Welte646ecdb2017-12-28 03:21:57 +0100812 call_id := int2hex(ep_nr, 2) & '1234'H;
813 f_crcx_and_dlcx_ep_callid_connid(ep, call_id);
814 }
815 }
816
Harald Welte79181ff2017-11-18 19:26:11 +0100817 /* Test (valid) CRCX followed by (valid) DLCX containing EP+CallId */
818 testcase TC_crcx_and_dlcx_ep_callid() runs on dummy_CT {
Harald Welte5b4c44e2017-09-17 16:35:27 +0800819 var template MgcpCommand cmd;
820 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100821 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100822 var MgcpCallId call_id := '51233'H;
Harald Welte5b4c44e2017-09-17 16:35:27 +0800823
Harald Welteedc45c12017-11-18 19:15:05 +0100824 f_init(ep);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800825
Harald Welteba62c8c2017-11-18 18:26:49 +0100826 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
Harald Welte9988d282017-11-18 19:22:00 +0100827 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800828
Harald Welteba62c8c2017-11-18 18:26:49 +0100829 f_dlcx_ok(ep, call_id);
Harald Welte5b4c44e2017-09-17 16:35:27 +0800830
831 setverdict(pass);
832 }
833
Harald Welte79181ff2017-11-18 19:26:11 +0100834 /* Test (valid) CRCX followed by (valid) DLCX containing EP */
835 testcase TC_crcx_and_dlcx_ep() runs on dummy_CT {
836 var template MgcpCommand cmd;
837 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100838 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100839 var MgcpCallId call_id := '51232'H;
Harald Welte79181ff2017-11-18 19:26:11 +0100840
841 f_init(ep);
842
843 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
844 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
845
846 f_dlcx_ok(ep);
847
848 setverdict(pass);
849 }
850
851
Harald Welte6d167f82017-11-18 19:41:35 +0100852 /* CRCX + DLCX of valid endpoint but invalid call-id */
853 testcase TC_crcx_and_dlcx_ep_callid_inval() runs on dummy_CT {
854 var template MgcpCommand cmd;
855 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100856 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100857 var MgcpCallId call_id := '51231'H;
858
859 f_init(ep);
860
861 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
862 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
863
864 f_dlcx(ep, "516", *, 'ffff'H);
865
866 setverdict(pass);
867 }
868
869
870 /* CRCX + DLCX of valid endpoint and call-id but invalid conn-id */
871 testcase TC_crcx_and_dlcx_ep_callid_connid_inval() runs on dummy_CT {
872 var template MgcpCommand cmd;
873 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100874 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Welte6d167f82017-11-18 19:41:35 +0100875 var MgcpCallId call_id := '51230'H;
876
877 f_init(ep);
878
879 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
880 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
881
882 f_dlcx(ep, "515", *, call_id, 'ffff'H);
883
884 setverdict(pass);
885 }
886
887
Harald Weltee636afd2017-09-17 16:24:09 +0800888 /* TODO: Double-DLCX (retransmission) */
Harald Weltef53f1642017-11-18 19:57:11 +0100889 testcase TC_crcx_and_dlcx_retrans() runs on dummy_CT {
890 var template MgcpCommand cmd;
891 var MgcpResponse resp;
Philipp Maierbb7a01c2018-02-01 12:32:57 +0100892 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "5@" & c_mgw_domain;
Harald Weltef53f1642017-11-18 19:57:11 +0100893 var MgcpCallId call_id := '51229'H;
894 var template MgcpResponse rtmpl := {
895 line := {
896 code := "200",
897 string := "OK"
898 },
899 params:= { },
900 sdp := omit
901 };
902
903 f_init(ep);
904
905 cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id);
906 resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
907
908 cmd := ts_DLCX(get_next_trans_id(), ep, call_id);
909 resp := mgcp_transceive_mgw(cmd, rtmpl);
910 resp := mgcp_transceive_mgw(cmd, rtmpl);
911
912 setverdict(pass);
913 }
914
Harald Weltebb7523b2018-03-29 08:52:01 +0200915 template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt,
916 charstring codec) := {
917 em := {
918 hostname := host_a,
919 portnr := omit
920 },
921 mgw := {
922 hostname := host_b,
923 portnr := omit
924 },
925 pt := pt,
926 codec := codec
927 }
Harald Weltef53f1642017-11-18 19:57:11 +0100928
Harald Weltebb7523b2018-03-29 08:52:01 +0200929 /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */
930 testcase TC_rtpem_selftest() runs on dummy_CT {
931 var RtpemStats stats[2];
932 var integer local_port := 10000;
933 var integer local_port2 := 20000;
934
935 f_init();
936
937 f_rtpem_bind(RTPEM[0], "127.0.0.1", local_port);
938 f_rtpem_bind(RTPEM[1], "127.0.0.2", local_port2);
939
940 f_rtpem_connect(RTPEM[0], "127.0.0.2", local_port2);
941 f_rtpem_connect(RTPEM[1], "127.0.0.1", local_port);
942
943 log("=== starting");
944 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
945 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
946
947 f_sleep(5.0);
948
949 log("=== stopping");
950 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
951 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
952 f_sleep(0.5);
953 f_rtpem_mode(RTPEM[1], RTPEM_MODE_NONE);
954 f_rtpem_mode(RTPEM[0], RTPEM_MODE_NONE);
955
956 stats[0] := f_rtpem_stats_get(RTPEM[0]);
957 stats[1] := f_rtpem_stats_get(RTPEM[1]);
958 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
959 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +0200960 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +0200961 }
962 setverdict(pass);
963 }
964
Philipp Maier2321ef92018-06-27 17:52:04 +0200965 /* Create one half open connection in receive-only mode. The MGW must accept
966 * the packets but must not send any. */
967 testcase TC_one_crcx_receive_only_rtp() runs on dummy_CT {
968 var RtpFlowData flow;
969 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
970 var MgcpCallId call_id := '1225'H;
971 var RtpemStats stats;
972
973 f_init(ep);
974 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000/1"));
975 flow.em.portnr := 10000;
976 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow, false);
977
978 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
979 f_sleep(1.0);
980 f_flow_delete(RTPEM[0], ep, call_id);
981
982 stats := f_rtpem_stats_get(RTPEM[0]);
983
984 if (stats.num_pkts_tx < 40) {
985 setverdict(fail);
986 }
987 if (stats.bytes_payload_tx < 190) {
988 setverdict(fail);
989 }
Philipp Maier36291392018-07-25 09:40:44 +0200990
991 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +0200992
993 setverdict(pass);
994 }
995
996 /* Create one connection in loopback mode, test if the RTP packets are
997 * actually reflected */
998 testcase TC_one_crcx_loopback_rtp() runs on dummy_CT {
999 var RtpFlowData flow;
1000 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain;
1001 var MgcpCallId call_id := '1225'H;
1002 var RtpemStats stats;
1003
1004 f_init(ep);
1005 flow := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000/1"));
1006 flow.em.portnr := 10000;
1007 f_flow_create(RTPEM[0], ep, call_id, "loopback", flow);
1008
1009 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1010 f_sleep(1.0);
1011 f_flow_delete(RTPEM[0], ep, call_id);
1012
1013 stats := f_rtpem_stats_get(RTPEM[0]);
1014
1015 if (stats.num_pkts_tx != stats.num_pkts_rx) {
1016 setverdict(fail);
1017 }
1018 if (stats.bytes_payload_tx != stats.bytes_payload_rx) {
1019 setverdict(fail);
1020 }
Philipp Maier36291392018-07-25 09:40:44 +02001021
1022 f_rtpem_stats_err_check(stats);
Philipp Maier2321ef92018-06-27 17:52:04 +02001023
1024 setverdict(pass);
1025 }
1026
Philipp Maier7df85f62018-07-25 10:26:09 +02001027 function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a,
1028 charstring codec_name_b, integer pt_b) runs on dummy_CT {
Harald Weltebb7523b2018-03-29 08:52:01 +02001029 var RtpFlowData flow[2];
1030 var RtpemStats stats[2];
Harald Weltebb7523b2018-03-29 08:52:01 +02001031 var MgcpResponse resp;
1032 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1033 var MgcpCallId call_id := '1226'H;
Philipp Maier2321ef92018-06-27 17:52:04 +02001034 var integer tolerance := 0;
Harald Weltebb7523b2018-03-29 08:52:01 +02001035
1036 f_init(ep);
1037
1038 /* from us to MGW */
Philipp Maier7df85f62018-07-25 10:26:09 +02001039 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_a, codec_name_a));
Harald Weltebb7523b2018-03-29 08:52:01 +02001040 /* bind local RTP emulation sockets */
1041 flow[0].em.portnr := 10000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001042 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001043
1044 /* from MGW back to us */
Philipp Maier7df85f62018-07-25 10:26:09 +02001045 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, pt_b, codec_name_b));
Harald Weltebb7523b2018-03-29 08:52:01 +02001046 flow[1].em.portnr := 20000;
Philipp Maier2321ef92018-06-27 17:52:04 +02001047 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1048
1049 if (bidir) {
1050 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1051 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1052
1053 /* Note: When we test bidirectional we may
1054 * loose packets during switch off because
1055 * both ends are transmitting and we only
1056 * can switch them off one by one. */
1057 tolerance := 3;
1058 } else {
1059 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
1060 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1061 }
1062
1063 f_sleep(1.0);
1064
1065 f_flow_delete(RTPEM[1]);
1066 f_flow_delete(RTPEM[0], ep, call_id);
1067
1068 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1069 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1070 if (not f_rtpem_stats_compare(stats[0], stats[1], tolerance)) {
1071 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001072 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001073 }
1074
Philipp Maier36291392018-07-25 09:40:44 +02001075 f_rtpem_stats_err_check(stats[0]);
1076 f_rtpem_stats_err_check(stats[1]);
Philipp Maierc290d722018-07-24 18:51:36 +02001077
Philipp Maier2321ef92018-06-27 17:52:04 +02001078 setverdict(pass);
1079 }
1080
1081 /* create two local RTP emulations; create two connections on MGW EP, exchange some data */
1082 testcase TC_two_crcx_and_rtp() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001083 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 98);
Philipp Maier2321ef92018-06-27 17:52:04 +02001084 }
1085
1086 /* create two local RTP emulations; create two connections on MGW EP,
1087 * exchange some data in both directions */
1088 testcase TC_two_crcx_and_rtp_bidir() runs on dummy_CT {
Philipp Maier7df85f62018-07-25 10:26:09 +02001089 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 98);
1090 }
1091
1092 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1093 testcase TC_two_crcx_diff_pt_and_rtp() runs on dummy_CT {
1094 f_TC_two_crcx_and_rtp(false, "AMR/8000", 98, "AMR/8000", 112);
1095 }
1096
1097 /* same as TC_two_crcx_and_rtp, but with different PT number on both ends */
1098 testcase TC_two_crcx_diff_pt_and_rtp_bidir() runs on dummy_CT {
1099 f_TC_two_crcx_and_rtp(true, "AMR/8000", 98, "AMR/8000", 112);
Philipp Maier2321ef92018-06-27 17:52:04 +02001100 }
1101
1102 /* create two local RTP emulations and pass data in both directions */
1103 testcase TC_two_crcx_mdcx_and_rtp() runs on dummy_CT {
1104 var RtpFlowData flow[2];
1105 var RtpemStats stats[2];
1106 var MgcpResponse resp;
1107 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1108 var MgcpCallId call_id := '1227'H;
1109 var integer num_pkts_tx[2];
1110 var integer temp;
1111
1112 f_init(ep);
1113
1114 /* Create the first connection in receive only mode */
1115 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1116 flow[0].em.portnr := 10000;
1117 f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], false);
1118
1119 /* Create the second connection. This connection will be also
1120 * in receive only mode */
1121 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 3, "GSM/8000/1"));
1122 flow[1].em.portnr := 20000;
1123 f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], false);
1124
1125 /* The first leg starts transmitting */
1126 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1127 f_sleep(0.5);
1128 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1129 if (stats[0].num_pkts_rx_err_disabled != 0) {
1130 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001131 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001132 }
1133 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1134 if (stats[1].num_pkts_rx_err_disabled != 0) {
1135 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001136 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001137 }
1138
1139 /* The second leg starts transmitting a little later */
1140 f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY);
1141 f_sleep(1.0);
1142 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1143 if (stats[0].num_pkts_rx_err_disabled != 0) {
1144 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001145 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001146 }
1147 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1148 if (stats[1].num_pkts_rx_err_disabled != 0) {
1149 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001150 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001151 }
1152
1153 /* The first leg will now be switched into bidirectional
1154 * mode, but we do not expect any data comming back yet. */
1155 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1156 f_sleep(0.5);
1157 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1158 if (stats[1].num_pkts_rx_err_disabled != 0) {
1159 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001160 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001161 }
1162 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1163 if (stats[1].num_pkts_rx_err_disabled != 0) {
1164 setverdict(fail, "received packets from MGW on recvonly connection");
Daniel Willmannafce8662018-07-06 23:11:32 +02001165 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001166 }
1167
1168 /* When the second leg is switched into bidirectional mode
1169 * as well, then the MGW will connect the two together and
1170 * we should see RTP streams passing through from both ends. */
1171 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1172 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1173 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1174 num_pkts_tx[0] := stats[0].num_pkts_tx
1175 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1176 num_pkts_tx[1] := stats[1].num_pkts_tx
1177
1178 f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1179 f_sleep(2.0);
1180
1181 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1182 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1183
1184 temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx;
1185 if (temp > 3 or temp < -3) {
1186 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001187 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001188 }
1189
1190 temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx;
1191 if (temp > 3 or temp < -3) {
1192 setverdict(fail, "number of packets not within normal parameters");
Daniel Willmannafce8662018-07-06 23:11:32 +02001193 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001194 }
Philipp Maier36291392018-07-25 09:40:44 +02001195
1196 f_rtpem_stats_err_check(stats[0]);
1197 f_rtpem_stats_err_check(stats[1]);
Philipp Maier2321ef92018-06-27 17:52:04 +02001198
1199 /* Tear down */
1200 f_flow_delete(RTPEM[0]);
1201 f_flow_delete(RTPEM[1], ep, call_id);
1202 setverdict(pass);
1203 }
1204
1205 /* Test what happens when two RTP streams from different sources target
1206 * a single connection. Is the unsolicited stream properly ignored? */
1207 testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT {
1208 var RtpFlowData flow[2];
1209 var RtpemStats stats[2];
1210 var MgcpResponse resp;
1211 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1212 var MgcpCallId call_id := '1234321326'H;
1213 var integer unsolicited_port := 10002;
1214
1215 f_init(ep);
1216
1217 /* from us to MGW */
1218 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1219 /* bind local RTP emulation sockets */
1220 flow[0].em.portnr := 10000;
1221 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1222
1223 /* from MGW back to us */
1224 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 98, "AMR/8000"));
1225 flow[1].em.portnr := 20000;
1226 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
Harald Weltebb7523b2018-03-29 08:52:01 +02001227
1228 f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);
1229 f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY);
1230
Philipp Maier2321ef92018-06-27 17:52:04 +02001231 f_sleep(0.5);
Harald Weltebb7523b2018-03-29 08:52:01 +02001232
Philipp Maier2321ef92018-06-27 17:52:04 +02001233 /* Start inserting unsolicited RTP packets */
Philipp Maier4de3e372018-06-29 17:15:22 +02001234 f_rtpem_bind(RTPEM[2], mp_local_ip, unsolicited_port);
1235 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001236 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1237
1238 f_sleep(0.5);
1239
Daniel Willmanna069d382018-12-13 13:53:33 +01001240 /* Stop transmitting packets and tear down the flows */
1241 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
Philipp Maier2321ef92018-06-27 17:52:04 +02001242 f_flow_delete(RTPEM[0]);
1243 f_flow_delete(RTPEM[1], ep, call_id);
Harald Weltebb7523b2018-03-29 08:52:01 +02001244
1245 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1246 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1247 if (not f_rtpem_stats_compare(stats[0], stats[1])) {
1248 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001249 mtc.stop;
Harald Weltebb7523b2018-03-29 08:52:01 +02001250 }
1251
Philipp Maier36291392018-07-25 09:40:44 +02001252 f_rtpem_stats_err_check(stats[0]);
1253 f_rtpem_stats_err_check(stats[0]);
Philipp Maierc290d722018-07-24 18:51:36 +02001254
Harald Weltebb7523b2018-03-29 08:52:01 +02001255 setverdict(pass);
Philipp Maier2321ef92018-06-27 17:52:04 +02001256 }
Harald Weltebb7523b2018-03-29 08:52:01 +02001257
Philipp Maier2321ef92018-06-27 17:52:04 +02001258 /* Test a handover situation. We first create two connections transmit
1259 * some data bidirectionally. Then we will simulate a handover situation. */
1260 testcase TC_two_crcx_and_one_mdcx_rtp_ho() runs on dummy_CT {
1261 var RtpFlowData flow[2];
1262 var RtpemStats stats[3];
1263 var MgcpResponse resp;
1264 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "4@" & c_mgw_domain;
1265 var MgcpCallId call_id := '76338'H;
1266 var integer port_old;
1267
1268 f_init(ep);
1269
1270 /* First connection (BTS) */
1271 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1272 /* bind local RTP emulation sockets */
1273 flow[0].em.portnr := 10000;
1274 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1275
1276 /* Second connection (PBX) */
1277 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 110, "GSM-EFR/8000"));
1278 flow[1].em.portnr := 20000;
1279 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1280
1281 /* Normal rtp flow for one second */
1282 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1283 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1284 f_sleep(1.0);
1285
1286 /* Now switch the flow over to a new port (BTS) */
1287 port_old := flow[0].em.portnr;
1288 flow[0].em.portnr := 10002;
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001289 f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY);
Philipp Maier2321ef92018-06-27 17:52:04 +02001290 f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
Daniel Willmann43a1a0b2018-08-01 11:19:39 +02001291 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
Philipp Maier2321ef92018-06-27 17:52:04 +02001292
1293 /* When handing over a call, the old source may still keep
1294 * transmitting for a while. We simulate this by injecting
1295 * some unsolicited packets on the behalf of the old source,
1296 * (old remote port) */
Philipp Maier4de3e372018-06-29 17:15:22 +02001297 f_rtpem_bind(RTPEM[2], mp_local_ip, port_old);
1298 f_rtpem_connect(RTPEM[2], mp_remote_ip, flow[0].mgw.portnr);
Philipp Maier2321ef92018-06-27 17:52:04 +02001299 f_rtpem_mode(RTPEM[2], RTPEM_MODE_TXONLY);
1300 f_sleep(1.0);
1301 f_rtpem_mode(RTPEM[2], RTPEM_MODE_NONE);
1302 f_sleep(1.0);
1303
1304 /* Terminate call */
1305 f_flow_delete(RTPEM[0]);
1306 f_flow_delete(RTPEM[1], ep, call_id);
1307
1308 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1309 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1310 if (not f_rtpem_stats_compare(stats[0], stats[1], 5)) {
1311 setverdict(fail, "RTP endpoint statistics don't match");
Daniel Willmannafce8662018-07-06 23:11:32 +02001312 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001313 }
1314 stats[2] := f_rtpem_stats_get(RTPEM[2]);
1315 if (stats[2].num_pkts_rx_err_disabled != 0) {
1316 setverdict(fail, "received packets on old leg after handover");
Daniel Willmannafce8662018-07-06 23:11:32 +02001317 mtc.stop;
Philipp Maier2321ef92018-06-27 17:52:04 +02001318 }
1319
Philipp Maier36291392018-07-25 09:40:44 +02001320 f_rtpem_stats_err_check(stats[0]);
1321 f_rtpem_stats_err_check(stats[1]);
1322 f_rtpem_stats_err_check(stats[2]);
Philipp Maierc290d722018-07-24 18:51:36 +02001323
Philipp Maier2321ef92018-06-27 17:52:04 +02001324 setverdict(pass);
Harald Weltebb7523b2018-03-29 08:52:01 +02001325 }
Harald Weltef53f1642017-11-18 19:57:11 +01001326
Philipp Maier6d4e0942019-02-21 17:35:01 +01001327
1328 /* create two local RTP emulations; create two connections on MGW EP, see if
1329 * exchanged data is converted bwtween ts101318 and rfc5993 */
1330 testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT {
1331 var RtpFlowData flow[2];
1332 var RtpemStats stats[2];
1333 var MgcpResponse resp;
1334 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1335 var MgcpCallId call_id := '1226'H;
1336
1337 f_init(ep);
1338
1339 /* Turn on conversion mode */
1340 f_vty_enter_config(MGWVTY);
1341 f_vty_transceive(MGWVTY, "mgcp");
1342 f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr");
1343
1344 /* from us to MGW */
1345 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1346 /* bind local RTP emulation sockets */
1347 flow[0].em.portnr := 10000;
1348 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1349 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1350 flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1351 flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O;
1352 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1353
1354 /* from MGW back to us */
1355 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 111, "GSM-HR-08/8000"));
1356 flow[1].em.portnr := 20000;
1357 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1358 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1359 flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1360 flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O;
1361 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1362
1363 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1364 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1365
1366 f_sleep(1.0);
1367
1368 f_flow_delete(RTPEM[0]);
1369 f_flow_delete(RTPEM[1], ep, call_id);
1370
1371 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1372 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1373
1374 f_rtpem_stats_err_check(stats[0]);
1375 f_rtpem_stats_err_check(stats[1]);
1376
1377 /* Turn off conversion mode */
1378 f_vty_transceive(MGWVTY, "no rtp-patch rfc5993hr");
1379
1380 setverdict(pass);
1381 }
1382
Philipp Maier4f764ce2019-03-07 10:54:10 +01001383 /* create two local RTP emulations; create two connections on MGW EP, see if
1384 * exchanged data is converted between AMR octet-aligned and bandwith
1385 * efficient-mode */
1386 function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT {
1387 var RtpFlowData flow[2];
1388 var RtpemStats stats[2];
1389 var MgcpResponse resp;
1390 var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain;
1391 var MgcpCallId call_id := '1226'H;
1392
1393 f_init(ep);
1394
1395 /* from us to MGW */
1396 flow[0] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1397 /* bind local RTP emulation sockets */
1398 flow[0].em.portnr := 10000;
1399 flow[0].rtp_cfg := c_RtpemDefaultCfg;
1400 flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
1401 flow[0].rtp_cfg.rx_fixed_payload := pl0;
1402 flow[0].rtp_cfg.tx_fixed_payload := pl0;
1403 flow[0].fmtp := fmtp0;
1404 f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
1405
1406 /* from MGW back to us */
1407 flow[1] := valueof(t_RtpFlow(mp_local_ip, mp_remote_ip, 112, "AMR/8000"));
1408 flow[1].em.portnr := 20000;
1409 flow[1].rtp_cfg := c_RtpemDefaultCfg;
1410 flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
1411 flow[1].rtp_cfg.rx_fixed_payload := pl1;
1412 flow[1].rtp_cfg.tx_fixed_payload := pl1;
1413 flow[1].fmtp := fmtp1;
1414 f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
1415
1416 f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR);
1417 f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR);
1418
1419 f_sleep(1.0);
1420
1421 f_flow_delete(RTPEM[0]);
1422 f_flow_delete(RTPEM[1], ep, call_id);
1423
1424 stats[0] := f_rtpem_stats_get(RTPEM[0]);
1425 stats[1] := f_rtpem_stats_get(RTPEM[1]);
1426
1427 f_rtpem_stats_err_check(stats[0]);
1428 f_rtpem_stats_err_check(stats[1]);
1429
1430 setverdict(pass);
1431 }
1432
1433 testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT {
1434 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=1", "octet-align=0");
1435 }
1436
1437 testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT {
1438 f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1");
1439 }
1440
1441 testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT {
1442 f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0");
1443 }
Philipp Maier6d4e0942019-02-21 17:35:01 +01001444
Harald Weltee636afd2017-09-17 16:24:09 +08001445 /* TODO: Double-DLCX (no retransmission) */
1446
1447
1448
1449 /* TODO: AUEP (various) */
1450 /* TODO: RSIP (various) */
1451 /* TODO: RQNT (various) */
1452 /* TODO: EPCF (various) */
1453 /* TODO: AUCX (various) */
1454 /* TODO: invalid verb (various) */
1455
Harald Welte00a067f2017-09-13 23:27:17 +02001456 control {
1457 execute(TC_selftest());
Harald Welte3c6ebb92017-09-16 00:56:57 +08001458 execute(TC_crcx());
Philipp Maier45635f42018-06-05 17:28:02 +02001459 execute(TC_crcx_no_lco());
Philipp Maierbb7a01c2018-02-01 12:32:57 +01001460 execute(TC_crcx_noprefix());
Harald Weltee636afd2017-09-17 16:24:09 +08001461 execute(TC_crcx_unsupp_mode());
1462 execute(TC_crcx_early_bidir_mode());
1463 execute(TC_crcx_unsupp_param());
1464 execute(TC_crcx_missing_callid());
1465 execute(TC_crcx_missing_mode());
1466 execute(TC_crcx_unsupp_packet_intv());
1467 execute(TC_crcx_illegal_double_lco());
1468 execute(TC_crcx_sdp());
Philipp Maier5e06cee2018-02-01 18:28:08 +01001469 execute(TC_crcx_wildcarded());
1470 execute(TC_crcx_wildcarded_exhaust());
Harald Weltee636afd2017-09-17 16:24:09 +08001471 execute(TC_mdcx_without_crcx());
1472 execute(TC_dlcx_without_crcx());
Philipp Maier8a3dc922018-02-02 14:55:12 +01001473 execute(TC_mdcx_wildcarded());
1474 execute(TC_dlcx_wildcarded());
Harald Welte79181ff2017-11-18 19:26:11 +01001475 execute(TC_crcx_and_dlcx_ep_callid_connid());
1476 execute(TC_crcx_and_dlcx_ep_callid());
1477 execute(TC_crcx_and_dlcx_ep());
Harald Welte6d167f82017-11-18 19:41:35 +01001478 execute(TC_crcx_and_dlcx_ep_callid_inval());
1479 execute(TC_crcx_and_dlcx_ep_callid_connid_inval());
Harald Weltef53f1642017-11-18 19:57:11 +01001480 execute(TC_crcx_and_dlcx_retrans());
Harald Welte33d82162017-12-28 03:21:57 +01001481
1482 execute(TC_crcx_dlcx_30ep());
Harald Weltebb7523b2018-03-29 08:52:01 +02001483
1484 execute(TC_rtpem_selftest());
Philipp Maier2321ef92018-06-27 17:52:04 +02001485
1486 execute(TC_one_crcx_receive_only_rtp());
1487 execute(TC_one_crcx_loopback_rtp());
Harald Weltebb7523b2018-03-29 08:52:01 +02001488 execute(TC_two_crcx_and_rtp());
Philipp Maier2321ef92018-06-27 17:52:04 +02001489 execute(TC_two_crcx_and_rtp_bidir());
Philipp Maier7df85f62018-07-25 10:26:09 +02001490 execute(TC_two_crcx_diff_pt_and_rtp());
1491 execute(TC_two_crcx_diff_pt_and_rtp_bidir());
Philipp Maier2321ef92018-06-27 17:52:04 +02001492 execute(TC_two_crcx_mdcx_and_rtp());
1493 execute(TC_two_crcx_and_unsolicited_rtp());
1494 execute(TC_two_crcx_and_one_mdcx_rtp_ho());
Philipp Maier6d4e0942019-02-21 17:35:01 +01001495 execute(TC_ts101318_rfc5993_rtp_conversion());
Philipp Maier4f764ce2019-03-07 10:54:10 +01001496 execute(TC_amr_oa_bwe_rtp_conversion());
1497 execute(TC_amr_oa_oa_rtp_conversion());
1498 execute(TC_amr_bwe_bwe_rtp_conversion());
Harald Welte00a067f2017-09-13 23:27:17 +02001499 }
1500}