diff options
Diffstat (limited to 'mgw/MGCP_Test.ttcn')
-rw-r--r-- | mgw/MGCP_Test.ttcn | 1316 |
1 files changed, 1147 insertions, 169 deletions
diff --git a/mgw/MGCP_Test.ttcn b/mgw/MGCP_Test.ttcn index f6dfe57e..7d8c2352 100644 --- a/mgw/MGCP_Test.ttcn +++ b/mgw/MGCP_Test.ttcn @@ -10,19 +10,23 @@ */ module MGCP_Test { + import from Misc_Helpers all; import from Osmocom_Types all; import from MGCP_Types all; import from MGCP_Templates all; import from SDP_Types all; + import from SDP_Templates all; import from MGCP_CodecPort all; import from MGCP_CodecPort_CtrlFunct all; import from RTP_CodecPort all; import from RTP_CodecPort_CtrlFunct all; import from RTP_Emulation all; + import from IuUP_Emulation all; import from OSMUX_Types all; import from OSMUX_CodecPort all; import from OSMUX_CodecPort_CtrlFunct all; import from OSMUX_Emulation all; + import from AMR_Types all; import from IPL4asp_Types all; import from General_Types all; import from Native_Functions all; @@ -30,6 +34,13 @@ module MGCP_Test { import from IP_Types all; import from Osmocom_VTY_Functions all; import from TELNETasp_PortType all; + import from StatsD_Types all; + import from StatsD_CodecPort all; + import from StatsD_CodecPort_CtrlFunct all; + import from StatsD_Checker all; + import from Osmocom_CTRL_Functions all; + import from Osmocom_CTRL_Types all; + import from Osmocom_CTRL_Adapter all; const charstring c_mgw_domain := "mgw"; const charstring c_mgw_ep_rtpbridge := "rtpbridge/"; @@ -37,7 +48,7 @@ module MGCP_Test { /* any variables declared in the component will be available to * all functions that 'run on' the named component, similar to * class members in C++ */ - type component dummy_CT { + type component dummy_CT extends StatsD_ConnHdlr, CTRL_Adapter_CT { port MGCP_CODEC_PT MGCP; var boolean initialized := false; var ConnectionId g_mgcp_conn_id := -1; @@ -50,6 +61,8 @@ module MGCP_Test { port OsmuxEM_CTRL_PT OsmuxEM; port TELNETasp_PT MGWVTY; + + var StatsD_Checker_CT vc_STATSD; }; function get_next_trans_id() runs on dummy_CT return MgcpTransId { @@ -71,6 +84,10 @@ module MGCP_Test { charstring mp_remote_ipv6 := "::1"; PortNumber mp_local_rtp_port_base := 10000; PortNumber mp_local_osmux_port := 1985; + PortNumber mp_mgw_statsd_port := 8125; + PortNumber mp_mgw_ctrl_port := 4267; + /* Maximum number of available endpoints in osmo-mgw.cfg ("number endpoints"): */ + integer mp_num_endpoints := 300; } private function f_vty_enable_osmux(boolean osmux_on) runs on dummy_CT { @@ -141,8 +158,15 @@ module MGCP_Test { f_osmuxem_init(vc_OsmuxEM); connect(vc_OsmuxEM:CTRL, self:OsmuxEM); } + + f_init_statsd("VirtCallAgent", vc_STATSD, mp_local_ipv4, mp_mgw_statsd_port); + connect(self:STATSD_PROC, vc_STATSD:STATSD_PROC); + + f_statsd_reset(); } + f_ipa_ctrl_start_client(mp_remote_ipv4, mp_mgw_ctrl_port); + if (isvalue(ep)) { /* do a DLCX on all connections of the EP */ f_dlcx_ignore(valueof(ep)); @@ -313,18 +337,100 @@ module MGCP_Test { charstring hostname, integer portnr optional } + type record RtpOsmuxFlowData { + boolean local_cid_sent, /* whther non wildcarded CID was already sent to MGW */ + MgcpOsmuxCID local_cid optional, + MgcpOsmuxCID remote_cid optional, + OsmuxemConfig cfg optional + } + type record RtpCodecDescr { + uint7_t pt, + charstring codec, + charstring fmtp optional + } type record RtpFlowData { HostPort em, /* emulation side */ HostPort mgw, /* mgw side */ - uint7_t pt, - charstring codec, MgcpConnectionId mgcp_conn_id optional, + record of RtpCodecDescr codec_descr, RtpemConfig rtp_cfg optional, - boolean osmux_cid_sent, /* whther non wildcarded CID was already sent to MGW */ - MgcpOsmuxCID osmux_cid optional, - MgcpOsmuxCID osmux_cid_response optional, - OsmuxemConfig osmux_cfg optional, - charstring fmtp optional + RtpOsmuxFlowData osmux + } + + template RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt, + charstring codec, template charstring fmtp := omit) := { + em := { + hostname := host_a, + portnr := omit + }, + mgw := { + hostname := host_b, + portnr := omit + }, + codec_descr := {{ + pt := pt, + codec := codec, + fmtp := fmtp + }}, + osmux:= { + local_cid_sent := false, + local_cid := omit, + remote_cid := omit, + cfg := omit + } + } + + /* To be used with f_flow_create/modify... functions */ + function f_gen_sdp(RtpFlowData flow) runs on dummy_CT return SDP_Message { + var template SDP_Message sdp; + var SDP_fmt_list fmt_list := {}; + var SDP_attribute_list attributes := {}; + + /* Add SDP RTMAP attributes (codec type, referenced by PT) */ + for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) { + attributes := attributes & { valueof(ts_SDP_rtpmap(flow.codec_descr[i].pt, + flow.codec_descr[i].codec)) }; + } + + /* Add SDP PTIME attribute, regardless of which codec, the packet intervall remains the same */ + attributes := attributes & { valueof(ts_SDP_ptime(20)) }; + + /* Add SDP FMTP attributes (codec parameters for each codec, referenced by PT) */ + for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) { + if (isvalue(flow.codec_descr[i].fmtp) and flow.codec_descr[i].fmtp != "") { + attributes := attributes & { valueof(ts_SDP_fmtp(flow.codec_descr[i].pt, + flow.codec_descr[i].fmtp)) }; + } + } + + /* Final step: Generate SDP */ + for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) { + fmt_list := fmt_list & {int2str(flow.codec_descr[i].pt)}; + } + sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42", flow.em.portnr, fmt_list, attributes); + + return valueof(sdp); + } + + /* Generate a valid RTP emulation config from the payload type numbers configured in the flow description and + * the the default configuration of the RTP emulation module. */ + function f_gen_rtpem_config_from_flow(RtpFlowData flow) return RtpemConfig { + var RtpemConfig rtp_cfg := c_RtpemDefaultCfg; + + for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) { + var RtpemConfigPayload tx_cfg_payload; + var RtpemConfigPayload rx_cfg_payload; + + tx_cfg_payload.payload_type := flow.codec_descr[i].pt; + tx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.tx_payloads[0].fixed_payload; + rx_cfg_payload.payload_type := flow.codec_descr[i].pt; + rx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.rx_payloads[0].fixed_payload; + + rtp_cfg.tx_payloads := rtp_cfg.tx_payloads & {tx_cfg_payload}; + rtp_cfg.rx_payloads := rtp_cfg.rx_payloads & {rx_cfg_payload}; + } + + return rtp_cfg; } /* Create an RTP flow (bidirectional, or receive-only) */ @@ -333,12 +439,6 @@ module MGCP_Test { runs on dummy_CT { var template MgcpCommand cmd; var MgcpResponse resp; - var SDP_attribute_list attributes; - - attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) }; - if (isvalue(flow.fmtp)) { - attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) }; - } /* bind local RTP emulation socket */ f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr); @@ -347,9 +447,7 @@ module MGCP_Test { if (ispresent(flow.rtp_cfg)) { f_rtpem_configure(pt, flow.rtp_cfg); } else { - var RtpemConfig rtp_cfg := c_RtpemDefaultCfg; - rtp_cfg.tx_payload_type := flow.pt - f_rtpem_configure(pt, rtp_cfg); + f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow)); } if (one_phase) { @@ -359,8 +457,7 @@ module MGCP_Test { * one go. */ cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id); - cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42", - flow.em.portnr, { int2str(flow.pt) }, attributes); + cmd.sdp := f_gen_sdp(flow); resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK); flow.mgcp_conn_id := extract_conn_id(resp); @@ -391,27 +488,21 @@ module MGCP_Test { runs on dummy_CT { var template MgcpCommand cmd; var MgcpResponse resp; - var SDP_attribute_list attributes; var OsmuxTxHandle tx_hdl; var OsmuxRxHandle rx_hdl; var charstring cid_response; var OsmuxCID cid_resp_parsed - attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) }; - if (isvalue(flow.fmtp)) { - attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) }; - } - /* bind local Osmux emulation socket */ f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr); /* configure osmux-emulation */ - if (ispresent(flow.osmux_cfg)) { - f_osmuxem_configure(pt, flow.osmux_cfg); + if (ispresent(flow.osmux.cfg)) { + f_osmuxem_configure(pt, flow.osmux.cfg); } else { var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg; f_osmuxem_configure(pt, osmux_cfg); - flow.osmux_cfg := osmux_cfg + flow.osmux.cfg := osmux_cfg } if (one_phase) { @@ -419,16 +510,15 @@ module MGCP_Test { * part that tells the MGW where we are listening for Osmux streams * that come from the MGW. We get a fully working connection in * one go. */ - if (flow.osmux_cid != -1) { + if (flow.osmux.local_cid != -1) { /* We may still want to negotiate osmux CID later at MDCX */ rx_hdl := c_OsmuxemDefaultRxHandle; - rx_hdl.cid := flow.osmux_cid; + rx_hdl.cid := flow.osmux.local_cid; f_osmuxem_register_rxhandle(pt, rx_hdl); - flow.osmux_cid_sent := true; + flow.osmux.local_cid_sent := true; } - cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid); - cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42", - flow.em.portnr, { int2str(flow.pt) }, attributes); + cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid); + cmd.sdp := f_gen_sdp(flow); resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux); flow.mgcp_conn_id := extract_conn_id(resp); /* extract port number from response */ @@ -441,7 +531,7 @@ module MGCP_Test { * data back to us. In order to turn the connection in a fully * bi-directional one, a separate MDCX is needed. */ - cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux_cid); + cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid); resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux); flow.mgcp_conn_id := extract_conn_id(resp); @@ -461,12 +551,12 @@ module MGCP_Test { } /* Make sure response is no wildcard */ - flow.osmux_cid_response := f_mgcp_osmux_cid_decode(cid_response); - if (flow.osmux_cid_response == -1) { + flow.osmux.remote_cid := f_mgcp_osmux_cid_decode(cid_response); + if (flow.osmux.remote_cid == -1) { setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard"); mtc.stop; } - tx_hdl := valueof(t_TxHandleAMR590(flow.osmux_cid_response)); + tx_hdl := valueof(t_TxHandleAMR590(flow.osmux.remote_cid)); f_osmuxem_register_txhandle(pt, tx_hdl); /* finally, connect the emulation-side RTP socket to the MGW */ @@ -478,12 +568,6 @@ module MGCP_Test { runs on dummy_CT { var template MgcpCommand cmd; var MgcpResponse resp; - var SDP_attribute_list attributes; - - attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) }; - if (isvalue(flow.fmtp)) { - attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) }; - } /* rebind local RTP emulation socket to the new address */ f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr); @@ -492,15 +576,12 @@ module MGCP_Test { if (ispresent(flow.rtp_cfg)) { f_rtpem_configure(pt, flow.rtp_cfg); } else { - var RtpemConfig rtp_cfg := c_RtpemDefaultCfg; - rtp_cfg.tx_payload_type := flow.pt - f_rtpem_configure(pt, rtp_cfg); + f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow)); } /* connect MGW side RTP socket to the emulation-side RTP socket using SDP */ cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id); - cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42", - flow.em.portnr, { int2str(flow.pt) }, attributes); + cmd.sdp := f_gen_sdp(flow); resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK); /* extract MGW-side port number from response. (usually this @@ -517,39 +598,32 @@ module MGCP_Test { runs on dummy_CT { var template MgcpCommand cmd; var MgcpResponse resp; - var SDP_attribute_list attributes; var OsmuxRxHandle rx_hdl; var charstring cid_response; var OsmuxCID cid_resp_parsed - attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) }; - if (isvalue(flow.fmtp)) { - attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) }; - } - /* rebind local Osmux emulation socket to the new address */ f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr); /* configure osmux-emulation */ - if (ispresent(flow.osmux_cfg)) { - f_osmuxem_configure(pt, flow.osmux_cfg); + if (ispresent(flow.osmux.cfg)) { + f_osmuxem_configure(pt, flow.osmux.cfg); } else { var OsmuxemConfig osmux_cfg := c_OsmuxemDefaultCfg; f_osmuxem_configure(pt, osmux_cfg); } /* We didn't send a non-wildcarded Osmux CID yet. If caller wants to submit it, register handler */ - if (flow.osmux_cid_sent == false and flow.osmux_cid != -1) { + if (flow.osmux.local_cid_sent == false and flow.osmux.local_cid != -1) { rx_hdl := c_OsmuxemDefaultRxHandle; - rx_hdl.cid := flow.osmux_cid; + rx_hdl.cid := flow.osmux.local_cid; f_osmuxem_register_rxhandle(pt, rx_hdl); - flow.osmux_cid_sent := true; + flow.osmux.local_cid_sent := true; } /* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */ - cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux_cid); - cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42", - flow.em.portnr, { int2str(flow.pt) }, attributes); + cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux.local_cid); + cmd.sdp := f_gen_sdp(flow); resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK); /* extract MGW-side port number from response. (usually this @@ -573,7 +647,7 @@ module MGCP_Test { setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard"); mtc.stop; } - if (cid_resp_parsed != flow.osmux_cid_response) { + if (cid_resp_parsed != flow.osmux.remote_cid) { setverdict(fail, "Osmux CID in MGCP MDCX response changed from prev value"); mtc.stop; } @@ -614,6 +688,18 @@ module MGCP_Test { } } + /* Send an AuditEndpoint message to the MGW */ + function f_auep(charstring ep_prefix) runs on dummy_CT { + var MgcpEndpoint ep := ep_prefix & "@" & c_mgw_domain; + var template MgcpCommand cmd; + var MgcpResponse resp; + + f_init(ep); + + cmd := ts_AUEP(get_next_trans_id(), ep); + resp := mgcp_transceive_mgw(cmd, tr_AUEP_ACK); + } + function f_crcx(charstring ep_prefix) runs on dummy_CT { var MgcpEndpoint ep := ep_prefix & "2@" & c_mgw_domain; var template MgcpCommand cmd; @@ -699,6 +785,12 @@ module MGCP_Test { f_dlcx_ok(ep, call_id); } + /* test valid AUEP towards "null" endpoint */ + testcase TC_auep_null() runs on dummy_CT { + f_auep("null"); + setverdict(pass); + } + /* test valid CRCX without SDP */ testcase TC_crcx() runs on dummy_CT { f_crcx(c_mgw_ep_rtpbridge); @@ -918,18 +1010,19 @@ module MGCP_Test { } /* test valid wildcarded CRCX */ + type record of MgcpEndpoint MgcpEndpointList; testcase TC_crcx_wildcarded_exhaust() runs on dummy_CT { - const integer n_endpoints := 31; var integer i; var template MgcpCommand cmd; var MgcpResponse resp; var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain; var MgcpCallId call_id := '1234'H; - var MgcpEndpoint ep_assigned[n_endpoints]; + var MgcpEndpoint ep_assigned; + var MgcpEndpointList ep_assigned_li := {}; f_init(); /* Exhaust all endpoint resources on the virtual trunk */ - for (i := 0; i < n_endpoints; i := i+1) { + for (i := 0; i < mp_num_endpoints; i := i+1) { cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id); resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK); @@ -939,10 +1032,11 @@ module MGCP_Test { var MgcpMessage resp_msg := { response := resp } - if (f_mgcp_find_param(resp_msg, "Z", ep_assigned[i]) == false) { + if (f_mgcp_find_param(resp_msg, "Z", ep_assigned) == false) { setverdict(fail, "No SpecificEndpointName in MGCP response", resp); mtc.stop; } + ep_assigned_li := ep_assigned_li & {ep_assigned} } /* Try to allocate one more endpoint, which should fail */ @@ -952,8 +1046,8 @@ module MGCP_Test { setverdict(pass); /* clean-up */ - for (i := 0; i < n_endpoints; i := i+1) { - f_dlcx_ok(ep_assigned[i], call_id); + for (i := 0; i < mp_num_endpoints; i := i+1) { + f_dlcx_ok(ep_assigned_li[i], call_id); } setverdict(pass); } @@ -1013,6 +1107,28 @@ module MGCP_Test { setverdict(pass); } + /* DLCX to non existing endpoint */ + testcase TC_dlcx_non_existant_ep() runs on dummy_CT { + var template MgcpCommand cmd; + var MgcpResponse resp; + var charstring non_existant_ep := hex2str(int2hex(mp_num_endpoints + 1, 4)) + var MgcpEndpoint ep := c_mgw_ep_rtpbridge & non_existant_ep & "@" & c_mgw_domain; + var template MgcpResponse rtmpl := { + line := { + code := ("500"), + string := ? + }, + params:= { }, + sdp := omit + }; + + f_init(ep); + + cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H); + resp := mgcp_transceive_mgw(cmd, rtmpl); + setverdict(pass); + } + /* test valid wildcarded MDCX */ testcase TC_mdcx_wildcarded() runs on dummy_CT { /* Note: A wildcarded MDCX is not allowed, so we expect the @@ -1043,25 +1159,53 @@ module MGCP_Test { /* test valid wildcarded DLCX */ testcase TC_dlcx_wildcarded() runs on dummy_CT { - /* Note: A wildcarded DLCX is specified, but our MGW does not - * support this feature so we expect the MGW to reject the - * request */ var template MgcpCommand cmd; var MgcpResponse resp; var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain; + var integer i; + var MgcpCallId call_id := '1234'H; + var StatsDExpects expect; + f_init(ep); + + /* Allocate a few endpoints */ + for (i := 0; i < mp_num_endpoints; i := i+1) { + cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id); + resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK); + } + + /* Wait until the stats items have seteled and then check if we get the expected number (all) of + * occupied endpoints */ + f_sleep(1.0) + expect := { + { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := mp_num_endpoints, max := mp_num_endpoints} + }; + f_statsd_expect(expect); + + /* Send wildcarded DLCX */ var template MgcpResponse rtmpl := { line := { - code := "507", + code := "200", string := ? }, params:= { }, sdp := omit }; + cmd := ts_DLCX(get_next_trans_id(), ep); + mgcp_transceive_mgw(cmd, rtmpl); - f_init(ep); + /* Query a the statsd once to ensure that intermediate results are pulled from the + * pipeline. The second query (below) will return the actual result. */ + expect := { + { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := mp_num_endpoints} + }; + f_statsd_expect(expect); + + /* The second query must resturn a result with 0 endpoints in use. */ + expect := { + { name := "TTCN3.trunk.virtual-0.common.endpoints.used", mtype := "g", min := 0, max := 0} + }; + f_statsd_expect(expect); - cmd := ts_DLCX(get_next_trans_id(), ep, '41234'H); - resp := mgcp_transceive_mgw(cmd, rtmpl); setverdict(pass); } @@ -1101,6 +1245,51 @@ module MGCP_Test { setverdict(pass); } + /* test Creating 257 concurrent osmux conns. It should fail since maximum is 256. */ + testcase TC_crcx_osmux_257() runs on dummy_CT { + var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "*@" & c_mgw_domain; + var template MgcpCommand cmd; + var MgcpResponse resp; + var charstring cid_response; + var integer i; + + f_init(ep, true); + + for (i := 0; i < 256; i := i + 1) { + + cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1); + resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux); + extract_conn_id(resp); + + /* extract Osmux CID we got assigned by the MGW */ + var MgcpMessage resp_msg := { + response := resp + } + + if (f_mgcp_find_param(resp_msg, "X-OSMUX", cid_response) == false) { + setverdict(fail, "No Osmux CID in MGCP response", resp); + mtc.stop; + } + + /* Make sure response is no wildcard */ + if (f_mgcp_osmux_cid_decode(cid_response) == -1) { + setverdict(fail, "Osmux CID in MGCP response contains unexpected wildcard"); + mtc.stop; + } + } + + /* Now conn num 257, it should fail due to all Osmux conns already allocated: */ + cmd := ts_CRCX_osmux(get_next_trans_id(), ep, "recvonly", int2hex(i, 4), -1); + resp := mgcp_transceive_mgw(cmd, tr_MgcpResp_Err("400")); + + setverdict(pass); + + /* Clean up */ + for (i := 0; i < 256; i := i + 1) { + f_dlcx_ok(ep, int2hex(i, 4)); + } + } + /* Create one half open connection in receive-only mode. The MGW must accept * the packets but must not send any. */ testcase TC_one_crcx_receive_only_osmux() runs on dummy_CT { @@ -1113,7 +1302,7 @@ module MGCP_Test { f_init(ep, true); flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000/1")); flow.em.portnr := mp_local_osmux_port; - flow.osmux_cid := -1; + flow.osmux.local_cid := -1; f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow, false); /* create a transmitter not yet known by MGW */ @@ -1126,10 +1315,10 @@ module MGCP_Test { stats := f_osmuxem_stats_get(OsmuxEM); - if (stats.num_pkts_tx < 40 / flow.osmux_cfg.batch_size) { + if (stats.num_pkts_tx < 40 / flow.osmux.cfg.batch_size) { setverdict(fail); } - if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux_cfg.batch_size) { + if (stats.bytes_payload_tx < stats.num_pkts_tx * f_amrft_payload_len(tx_hdl.amr_ft) * flow.osmux.cfg.batch_size) { setverdict(fail); } @@ -1150,7 +1339,7 @@ module MGCP_Test { f_init(ep, true); flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1")); flow.em.portnr := mp_local_osmux_port; - flow.osmux_cid := 2; + flow.osmux.local_cid := 2; f_flow_create_osmux(OsmuxEM, ep, call_id, "loopback", flow); f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR); @@ -1217,7 +1406,7 @@ module MGCP_Test { return true; } - function f_TC_two_crcx_and_rtp_osmux(boolean bidir, + function f_TC_two_crcx_and_rtp_osmux(boolean bidir, boolean rtp_amr_oa, charstring local_ip_rtp, charstring remote_ip_rtp, charstring local_ip_osmux, charstring remote_ip_osmux) runs on dummy_CT { var RtpFlowData flow[2]; @@ -1228,15 +1417,35 @@ module MGCP_Test { var MgcpCallId call_id := '1226'H; var integer tolerance := 0; + var octetstring amr_payload; + var charstring fmtp; + f_init(ep, true); + var AMRFT cmr := AMR_FT_0; + var AMRFT ft := AMR_FT_2; + if (rtp_amr_oa) { + fmtp := "octet-align=1"; + var RTP_AMR_Hdr amr_oa_hdr := valueof(ts_RTP_AMR_Hdr(enum2int(cmr), enum2int(ft))); + amr_payload := enc_RTP_AMR_Hdr(amr_oa_hdr) & + f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload); + } else { + fmtp := "octet-align=0"; + /* Convert OA to BWE: */ + var RTP_AMR_BWE_Hdr amr_bwe_hdr := valueof(ts_RTP_AMR_BWE_Hdr(enum2int(cmr), enum2int(ft))); + var bitstring amr_bwe_hdr_bits := substr(oct2bit(enc_RTP_AMR_BWE_Hdr(amr_bwe_hdr)), 0 , 10); + var bitstring amr_data_bits := oct2bit(f_osmux_gen_expected_rx_rtp_payload(enum2int(ft), c_OsmuxemDefaultCfg.tx_fixed_payload)); + var bitstring amr_payload_bits := amr_bwe_hdr_bits & substr(amr_data_bits, 0, f_amrft_payload_bits_len(enum2int(ft))); + amr_payload := bit2oct(f_pad_bit(amr_payload_bits, (lengthof(amr_payload_bits)+7)/8*8, '0'B)); + }; + /* from us to MGW */ - flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000")); + flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000", fmtp)); flow[0].rtp_cfg := c_RtpemDefaultCfg - flow[0].rtp_cfg.tx_payload_type := flow[0].pt; - /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */ - flow[0].rtp_cfg.rx_fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload); - flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload; + flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.rx_payloads[0].fixed_payload := amr_payload; + flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.tx_payloads[0].fixed_payload := amr_payload; /* bind local RTP emulation sockets */ flow[0].em.portnr := 10000; f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]); @@ -1244,8 +1453,8 @@ module MGCP_Test { /* from MGW back to us */ flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000")); flow[1].em.portnr := mp_local_osmux_port; - flow[1].osmux_cid := 2; - flow[1].osmux_cfg := c_OsmuxemDefaultCfg; + flow[1].osmux.local_cid := 2; + flow[1].osmux.cfg := c_OsmuxemDefaultCfg; f_flow_create_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]); if (bidir) { @@ -1274,7 +1483,7 @@ module MGCP_Test { stats_rtp := f_rtpem_stats_get(RTPEM[0]); stats_osmux := f_osmuxem_stats_get(OsmuxEM); - if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux_cfg.batch_size, tolerance)) { + if (not f_rtp_osmux_stats_compare(stats_rtp, stats_osmux, flow[1].osmux.cfg.batch_size, tolerance)) { setverdict(fail, "RTP and Osmux endpoint statistics don't match"); mtc.stop; } @@ -1287,30 +1496,39 @@ module MGCP_Test { /* create one RTP and one OSmux emulations; create two connections on MGW EP, exchange some data */ testcase TC_two_crcx_and_rtp_osmux() runs on dummy_CT { - f_TC_two_crcx_and_rtp_osmux(false, mp_local_ipv4, mp_remote_ipv4, + f_TC_two_crcx_and_rtp_osmux(false, true, mp_local_ipv4, mp_remote_ipv4, mp_local_ipv4, mp_remote_ipv4); } /* create one RTP and one OSmux emulations; create two connections on MGW EP, * exchange some data in both directions */ testcase TC_two_crcx_and_rtp_osmux_bidir() runs on dummy_CT { - f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4, + f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4, + mp_local_ipv4, mp_remote_ipv4); + } + + /* create one RTP and one OSmux emulations; create two connections on MGW EP, + * exchange some data in both directions. RTP side is configured to + * rx/rx AMR in bandwidth-efficient mode. */ + testcase TC_two_crcx_and_rtp_osmux_bidir_amr_bwe() runs on dummy_CT { + f_TC_two_crcx_and_rtp_osmux(true, false, mp_local_ipv4, mp_remote_ipv4, mp_local_ipv4, mp_remote_ipv4); } + /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 */ testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6() runs on dummy_CT { - f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6, + f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6, mp_local_ipv6, mp_remote_ipv6); } /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv4 (RTP) and IPv6 (Osmux) */ testcase TC_two_crcx_and_rtp_osmux_bidir_ipv4_ipv6() runs on dummy_CT { - f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv4, mp_remote_ipv4, + f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv4, mp_remote_ipv4, mp_local_ipv6, mp_remote_ipv6); } /* Same as TC_two_crcx_and_rtp_osmux_bidir, but using IPv6 (RTP) and IPv4 (Osmux) */ testcase TC_two_crcx_and_rtp_osmux_bidir_ipv6_ipv4() runs on dummy_CT { - f_TC_two_crcx_and_rtp_osmux(true, mp_local_ipv6, mp_remote_ipv6, + f_TC_two_crcx_and_rtp_osmux(true, true, mp_local_ipv6, mp_remote_ipv6, mp_local_ipv4, mp_remote_ipv4); } @@ -1324,7 +1542,7 @@ module MGCP_Test { var MgcpResponse resp; var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain; var MgcpCallId call_id := '1227'H; - var integer num_pkts_tx[2]; + var integer num_pkts_tx[2], num_pkts_rx[2]; var integer temp; f_init(ep, true); @@ -1332,10 +1550,11 @@ module MGCP_Test { /* Create the first connection in receive only mode */ flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000")); flow[0].rtp_cfg := c_RtpemDefaultCfg - flow[0].rtp_cfg.tx_payload_type := flow[0].pt; + flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt; /* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */ - flow[0].rtp_cfg.rx_fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload); - flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload; + flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload); + flow[0].rtp_cfg.tx_payloads[0].fixed_payload := flow[0].rtp_cfg.rx_payloads[0].fixed_payload; /* bind local RTP emulation sockets */ flow[0].em.portnr := 10000; f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true); @@ -1346,11 +1565,11 @@ module MGCP_Test { flow[1] := valueof(t_RtpFlow(local_ip_osmux, remote_ip_osmux, 110, "AMR/8000")); flow[1].em.portnr := mp_local_osmux_port; if (crcx_osmux_wildcard) { - flow[1].osmux_cid := -1; + flow[1].osmux.local_cid := -1; } else { - flow[1].osmux_cid := 2; + flow[1].osmux.local_cid := 2; } - flow[1].osmux_cfg := c_OsmuxemDefaultCfg; + flow[1].osmux.cfg := c_OsmuxemDefaultCfg; f_flow_create_osmux(OsmuxEM, ep, call_id, "recvonly", flow[1], true); @@ -1385,51 +1604,68 @@ module MGCP_Test { /* The first leg will now be switched into bidirectional * mode, but we do not expect any data comming back yet. */ f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR); - stats_osmux := f_osmuxem_stats_get(OsmuxEM); - num_pkts_tx[1] := stats_osmux.num_pkts_tx; f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow[0]); + /* At this point in time, flow[1](Osmux)->MGW->flow[0](RTP) is active, + * hence if local CID was provided during CRCX we should already be seeing packets + * flowing in one direction, aka stats_rtp.num_pkts_rx sould be >0 after a while: */ f_sleep(0.5); stats_rtp := f_rtpem_stats_get(RTPEM[0]); if (stats_rtp.num_pkts_rx_err_disabled != 0) { setverdict(fail, "received packets from RTP MGW on recvonly connection"); mtc.stop; } + if (not crcx_osmux_wildcard and stats_rtp.num_pkts_rx == 0) { + setverdict(fail, "received 0 packets Osmux->MGW->RTP"); + mtc.stop; + } + stats_osmux := f_osmuxem_stats_get(OsmuxEM); if (stats_osmux.num_pkts_rx_err_disabled != 0) { setverdict(fail, "received packets from Osmux MGW on recvonly connection"); mtc.stop; } + if (stats_osmux.num_pkts_rx > 0) { + setverdict(fail, "received unexpected ", stats_osmux.num_pkts_rx, " packets RTP->MGW->Osmux"); + mtc.stop; + } /* When the second leg is switched into bidirectional mode * as well, then the MGW will connect the two together and * we should see RTP streams passing through from both ends. */ f_osmuxem_mode(OsmuxEM, OSMUXEM_MODE_BIDIR); - stats_rtp := f_rtpem_stats_get(RTPEM[0]); - num_pkts_tx[0] := stats_rtp.num_pkts_tx; - if (crcx_osmux_wildcard) { - /* For now we must set same CID as the MGW recvCID, - * having sendCID!=recvCID is not yet supported. */ - flow[1].osmux_cid := flow[1].osmux_cid_response; + /* We set now the local CID in MDCX: */ + flow[1].osmux.local_cid := 2; } f_flow_modify_osmux(OsmuxEM, ep, call_id, "sendrecv", flow[1]); + stats_rtp := f_rtpem_stats_get(RTPEM[0]); + stats_osmux := f_osmuxem_stats_get(OsmuxEM); + num_pkts_tx[0] := stats_rtp.num_pkts_tx; + num_pkts_tx[1] := stats_osmux.num_pkts_tx; + num_pkts_rx[0] := stats_rtp.num_pkts_rx; + num_pkts_rx[1] := stats_osmux.num_pkts_rx; f_sleep(2.0); stats_rtp := f_rtpem_stats_get(RTPEM[0]); stats_osmux := f_osmuxem_stats_get(OsmuxEM); - temp := stats_rtp.num_pkts_tx - num_pkts_tx[0] - stats_osmux.num_pkts_rx * flow[1].osmux_cfg.batch_size; - if (temp > 3 * flow[1].osmux_cfg.batch_size or temp < -3 * flow[1].osmux_cfg.batch_size) { + temp := (stats_rtp.num_pkts_tx - num_pkts_tx[0]) - + (stats_osmux.num_pkts_rx - num_pkts_rx[1]) * flow[1].osmux.cfg.batch_size; + if (temp > 3 * flow[1].osmux.cfg.batch_size or temp < -3 * flow[1].osmux.cfg.batch_size) { log("stats_rtp: ", stats_rtp); log("stats_osmux: ", stats_osmux); log("old_rtp_tx: ", num_pkts_tx[0]); - setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")"); + setverdict(fail, "RTP-Tx vs OSmux-Rx number of packets not within normal parameters (" & int2str(temp) & ")"); mtc.stop; } - temp := stats_osmux.num_pkts_tx - num_pkts_tx[1] - stats_rtp.num_pkts_rx / flow[1].osmux_cfg.batch_size; + temp := (stats_osmux.num_pkts_tx - num_pkts_tx[1]) - + ((stats_rtp.num_pkts_rx - num_pkts_rx[0])/ flow[1].osmux.cfg.batch_size); if (temp > 3 or temp < -3) { - setverdict(fail, "number of packets not within normal parameters (" & int2str(temp) & ")"); + log("stats_rtp: ", stats_rtp); + log("stats_osmux: ", stats_osmux); + log("old_osmux_tx: ", num_pkts_tx[1]); + setverdict(fail, "Osmux-Tx vs RTP-Rx number of packets not within normal parameters (" & int2str(temp) & ")"); mtc.stop; } @@ -1602,21 +1838,6 @@ module MGCP_Test { setverdict(pass); } - template (value) RtpFlowData t_RtpFlow(charstring host_a, charstring host_b, uint7_t pt, - charstring codec) := { - em := { - hostname := host_a, - portnr := omit - }, - mgw := { - hostname := host_b, - portnr := omit - }, - pt := pt, - codec := codec, - osmux_cid_sent := false - } - /* transmit RTP streams between two RTP Emulations back-to-back; expect no loss */ testcase TC_rtpem_selftest() runs on dummy_CT { var RtpemStats stats[2]; @@ -1693,16 +1914,16 @@ module MGCP_Test { /* Create one connection in loopback mode, test if the RTP packets are * actually reflected */ - testcase TC_one_crcx_loopback_rtp() runs on dummy_CT { + function f_TC_one_crcx_loopback_rtp(charstring local_ip, charstring remote_ip, boolean one_phase := true) runs on dummy_CT { var RtpFlowData flow; var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "1@" & c_mgw_domain; var MgcpCallId call_id := '1225'H; var RtpemStats stats; f_init(ep); - flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1")); + flow := valueof(t_RtpFlow(local_ip, remote_ip, 112, "GSM-HR-08/8000/1")); flow.em.portnr := 10000; - f_flow_create(RTPEM[0], ep, call_id, "loopback", flow); + f_flow_create(RTPEM[0], ep, call_id, "loopback", flow, one_phase := one_phase); f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR); f_sleep(1.0); @@ -1710,11 +1931,22 @@ module MGCP_Test { stats := f_rtpem_stats_get(RTPEM[0]); - if (stats.num_pkts_tx != stats.num_pkts_rx) { - setverdict(fail); - } - if (stats.bytes_payload_tx != stats.bytes_payload_rx) { - setverdict(fail); + if (one_phase) { + /* osmo-mgw knows both local and remote RTP address. Expect all packets to be reflected. */ + if (stats.num_pkts_tx != stats.num_pkts_rx) { + setverdict(fail); + } + if (stats.bytes_payload_tx != stats.bytes_payload_rx) { + setverdict(fail); + } + } else { + /* osmo-mgw knows only the local RTP address. Expect no packets to be reflected. */ + if (stats.num_pkts_rx > 0) { + setverdict(fail, "stats.num_pkts_rx=", stats.num_pkts_rx, ": osmo-mgw should not send RTP packets to an arbitrary peer"); + } + if (stats.bytes_payload_rx > 0) { + setverdict(fail); + } } f_rtpem_stats_err_check(stats); @@ -1722,6 +1954,24 @@ module MGCP_Test { setverdict(pass); } + /* Create one connection in loopback mode, test if the RTP packets are + * actually reflected */ + testcase TC_one_crcx_loopback_rtp() runs on dummy_CT { + f_TC_one_crcx_loopback_rtp(mp_local_ipv4, mp_remote_ipv4, one_phase := true) + } + testcase TC_one_crcx_loopback_rtp_ipv6() runs on dummy_CT { + f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := true) + } + + /* Same as above, but we will intenionally not tell the MGW where to + * send the outgoing traffic. The connection is still created in + * loopback mode, so the MGW should take the originating address from + * the incoming RTP packet and send it back to the source */ + testcase TC_one_crcx_loopback_rtp_implicit() runs on dummy_CT { + f_TC_one_crcx_loopback_rtp(mp_local_ipv6, mp_remote_ipv6, one_phase := false) + } + + function f_TC_two_crcx_and_rtp(boolean bidir, charstring codec_name_a, integer pt_a, charstring codec_name_b, integer pt_b) runs on dummy_CT { var RtpFlowData flow[2]; @@ -1799,7 +2049,9 @@ module MGCP_Test { /* create two local RTP emulations and pass data in both directions */ function f_tc_two_crcx_mdcx_and_rtp(charstring local_ip_a, charstring remote_ip_a, - charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT { + charstring local_ip_b, charstring remote_ip_b, + uint7_t payload_type := 3, + charstring codec_name := "GSM/8000/1") runs on dummy_CT { var RtpFlowData flow[2]; var RtpemStats stats[2]; var MgcpResponse resp; @@ -1811,13 +2063,13 @@ module MGCP_Test { f_init(ep); /* Create the first connection in receive only mode */ - flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 3, "GSM/8000/1")); + flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, payload_type, codec_name)); flow[0].em.portnr := 10000; f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true); /* Create the second connection. This connection will be also * in receive only mode */ - flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 3, "GSM/8000/1")); + flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, payload_type, codec_name)); flow[1].em.portnr := 20000; f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true); @@ -1915,6 +2167,13 @@ module MGCP_Test { mp_local_ipv6, mp_remote_ipv6); } + testcase TC_two_crcx_mdcx_and_rtp_clearmode() runs on dummy_CT { + f_tc_two_crcx_mdcx_and_rtp(mp_local_ipv4, mp_remote_ipv4, + mp_local_ipv4, mp_remote_ipv4, + 120, /* 3GPP TS 48.103 table 5.4.2.2.1 */ + "CLEARMODE/8000"); + } + /* Test what happens when two RTP streams from different sources target * a single connection. Is the unsolicited stream properly ignored? */ testcase TC_two_crcx_and_unsolicited_rtp() runs on dummy_CT { @@ -2037,10 +2296,62 @@ module MGCP_Test { setverdict(pass); } + testcase TC_two_crcx_confecho_sendonly_rtp() runs on dummy_CT { + var RtpFlowData flow[2]; + var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain; + var MgcpCallId call_id := '1225'H; + var RtpemStats stats[2]; + + f_init(ep); + + /* "Talker" is sending to MGW and receives echo. */ + flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "GSM-HR-08/8000/1")); + flow[0].em.portnr := 10000; + f_flow_create(RTPEM[0], ep, call_id, "confecho", flow[0]); + + /* "Listener" receives from MGW. */ + flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "GSM-HR-08/8000/1")); + flow[1].em.portnr := 20000; + f_flow_create(RTPEM[1], ep, call_id, "sendonly", flow[1]); + + + f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR); + f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR); + f_sleep(1.0); + f_flow_delete(RTPEM[0]); + f_flow_delete(RTPEM[1], ep, call_id); + + stats[0] := f_rtpem_stats_get(RTPEM[0]); + stats[1] := f_rtpem_stats_get(RTPEM[1]); + + /* The "Talker" will receive his RTP, so TX must match RX. + * As RTP from "Listener" is ignored, no extra packets shall be received. */ + if (stats[0].num_pkts_tx != stats[0].num_pkts_rx) { + setverdict(fail, "Talker does not receive as many packets as it transmits!"); + } + if (stats[0].bytes_payload_tx != stats[0].bytes_payload_rx) { + setverdict(fail, "Talker does not receive as many payload as it transmits!"); + } + + /* The "Listener" will also receive RTP of the "Talker", + * so TX of "Talker" must match RX of "Listener". */ + if (stats[0].num_pkts_tx != stats[1].num_pkts_rx) { + setverdict(fail, "Listener does not receive as many packets as talker transmits!"); + } + if (stats[0].bytes_payload_tx != stats[1].bytes_payload_rx) { + setverdict(fail, "Listener does not receive as many payload as talker transmits!"); + } + + f_rtpem_stats_err_check(stats[0]); + f_rtpem_stats_err_check(stats[1]); + + setverdict(pass); + } + /* create two local RTP emulations; create two connections on MGW EP, see if - * exchanged data is converted bwtween ts101318 and rfc5993 */ - testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT { + * exchanged data is converted between ts101318 and rfc5993 */ + function f_ts101318_rfc5993_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT { var RtpFlowData flow[2]; var RtpemStats stats[2]; var MgcpResponse resp; @@ -2055,22 +2366,24 @@ module MGCP_Test { f_vty_transceive(MGWVTY, "rtp-patch rfc5993hr"); /* Connection #0 (Bidirectional) */ - flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000")); + flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000", fmtp0)); /* bind local RTP emulation sockets */ flow[0].em.portnr := 10000; flow[0].rtp_cfg := c_RtpemDefaultCfg; - flow[0].rtp_cfg.tx_payload_type := flow[0].pt; - flow[0].rtp_cfg.rx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O; - flow[0].rtp_cfg.tx_fixed_payload := '0b11b3eede60be4e3ec68838c7b5'O; + flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0; + flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0; f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]); /* Connection #1 (Bidirectional) */ - flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000")); + flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000", fmtp1)); flow[1].em.portnr := 20000; flow[1].rtp_cfg := c_RtpemDefaultCfg; - flow[1].rtp_cfg.tx_payload_type := flow[1].pt; - flow[1].rtp_cfg.rx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O; - flow[1].rtp_cfg.tx_fixed_payload := '000b11b3eede60be4e3ec68838c7b5'O; + flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.rx_payloads[0].fixed_payload := pl1; + flow[1].rtp_cfg.tx_payloads[0].fixed_payload := pl1; f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]); /* Send RTP packets to connection #0, receive on connection #1 */ @@ -2107,10 +2420,23 @@ module MGCP_Test { setverdict(pass); } + const octetstring rtp_hr_gsm_ts101318 := '0b11b3eede60be4e3ec68838c7b5'O; + const octetstring rtp_hr_gsm_rfc5993 := '000b11b3eede60be4e3ec68838c7b5'O; + + testcase TC_ts101318_rfc5993_rtp_conversion() runs on dummy_CT { + f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "", ""); + } + + testcase TC_ts101318_rfc5993_rtp_conversion_fmtp() runs on dummy_CT { + f_ts101318_rfc5993_rtp_conversion(rtp_hr_gsm_ts101318, rtp_hr_gsm_rfc5993, "gsm-hr-format=ts101318", "gsm-hr-format=rfc5993"); + } + /* create two local RTP emulations; create two connections on MGW EP, see if - * exchanged data is converted between AMR octet-aligned and bandwith + * exchanged data is converted between AMR octet-aligned and bandwidth * efficient-mode */ - function f_TC_amr_x_x_rtp_conversion(octetstring pl0, octetstring pl1, charstring fmtp0, charstring fmtp1) runs on dummy_CT { + function f_TC_amr_x_x_rtp_conversion(charstring fmtp0, octetstring pl0, + charstring fmtp1a, octetstring pl1a, + charstring fmtp1b, octetstring pl1b) runs on dummy_CT { var RtpFlowData flow[2]; var RtpemStats stats[2]; var MgcpResponse resp; @@ -2120,24 +2446,36 @@ module MGCP_Test { f_init(ep); /* Connection #0 (Bidirectional) */ - flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000")); + flow[0] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 96, "AMR/8000", fmtp0)); /* bind local RTP emulation sockets */ flow[0].em.portnr := 10000; flow[0].rtp_cfg := c_RtpemDefaultCfg; - flow[0].rtp_cfg.tx_payload_type := flow[0].pt; - flow[0].rtp_cfg.rx_fixed_payload := pl0; - flow[0].rtp_cfg.tx_fixed_payload := pl0; - flow[0].fmtp := fmtp0; + flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0; + flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0; f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]); /* Connection #1 (Bidirectional) */ - flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000")); + flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000", fmtp1a)); flow[1].em.portnr := 20000; flow[1].rtp_cfg := c_RtpemDefaultCfg; - flow[1].rtp_cfg.tx_payload_type := flow[1].pt; - flow[1].rtp_cfg.rx_fixed_payload := pl1; - flow[1].rtp_cfg.tx_fixed_payload := pl1; - flow[1].fmtp := fmtp1; + flow[1].rtp_cfg.rx_payloads := {}; + flow[1].rtp_cfg.tx_payloads := {}; + if (pl1a != ''O) { + flow[1].rtp_cfg.rx_payloads := flow[1].rtp_cfg.rx_payloads & {{112, pl1a}}; + flow[1].rtp_cfg.tx_payloads := flow[1].rtp_cfg.tx_payloads & {{112, pl1a}}; + } + + /* The second fmtp parameter is to simulate a call agent that offers the transmission both modes. */ + if (fmtp1b != "") { + flow[1].codec_descr := flow[1].codec_descr & {{113, "AMR/8000", fmtp1b}}; + if (pl1b != ''O) { + flow[1].rtp_cfg.rx_payloads := flow[1].rtp_cfg.rx_payloads & {{113, pl1b}}; + flow[1].rtp_cfg.tx_payloads := flow[1].rtp_cfg.tx_payloads & {{113, pl1b}}; + } + } + f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]); /* Send RTP packets to connection #0, receive on connection #1 */ @@ -2175,16 +2513,46 @@ module MGCP_Test { * functions are real world AMR RTP payloads including AMR header. The * payloads were extracted from a trace with known good payloads. */ + const octetstring rtp_amr_5_90k_oa := '2014e959f35fdfe5e9667ffbc088818088'O; + const octetstring rtp_amr_5_90k_bwe := '217a567cd7f7f97a599ffef022206022'O; + const octetstring rtp_amr_5_15k_oa := '100c4e9ba850e30d5d53d04de41e7c'O; + const octetstring rtp_amr_5_15k_bwe := '10d3a6ea1438c35754f41379079f'O; + + /* Only one codec on each side */ testcase TC_amr_oa_bwe_rtp_conversion() runs on dummy_CT { - f_TC_amr_x_x_rtp_conversion('2014e959f35fdfe5e9667ffbc088818088'O, '217a567cd7f7f97a599ffef022206022'O, "octet-align=1", "octet-align=0"); + f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_90k_oa, "octet-align=0", rtp_amr_5_90k_bwe, "", ''O); } - testcase TC_amr_oa_oa_rtp_conversion() runs on dummy_CT { - f_TC_amr_x_x_rtp_conversion('100c4e9ba850e30d5d53d04de41e7c'O, '100c4e9ba850e30d5d53d04de41e7c'O, "octet-align=1", "octet-align=1"); + f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa, "octet-align=1", rtp_amr_5_15k_oa, "", ''O); } - testcase TC_amr_bwe_bwe_rtp_conversion() runs on dummy_CT { - f_TC_amr_x_x_rtp_conversion('10d3a6ea1438c35754f41379079f'O, '10d3a6ea1438c35754f41379079f'O, "octet-align=0", "octet-align=0"); + f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe, "octet-align=0", rtp_amr_5_15k_bwe, "", ''O); + } + + /* Only one codec on one side, two codecs (compatibility) on other side. The payloads are the same on both + * sides, so the expectation is that conversion must not be performed. Each test is done with both formats in + * two different configurations.*/ + testcase TC_amr_oa_oa_no_bwe_rtp_conversion() runs on dummy_CT { + f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa, + "octet-align=1", rtp_amr_5_15k_oa, + "octet-align=0", ''O); /* We expect to see NO bandwidth efficient packets! */ + } + testcase TC_amr_oa_no_bwe_oa_rtp_conversion() runs on dummy_CT { + /* (Same as above but flipped on the opposite side) */ + f_TC_amr_x_x_rtp_conversion("octet-align=1", rtp_amr_5_15k_oa, + "octet-align=0", ''O, /* We expect to see NO bandwidth efficient packets! */ + "octet-align=1", rtp_amr_5_15k_oa); + } + testcase TC_amr_bwe_bwe_no_oa_rtp_conversion() runs on dummy_CT { + f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe, + "octet-align=0", rtp_amr_5_15k_bwe, + "octet-align=1", ''O); /* We expect to see NO octet aligned packets! */ + } + testcase TC_amr_bwe_no_oa_bwe_rtp_conversion() runs on dummy_CT { + /* (Same as above but flipped on the opposite side) */ + f_TC_amr_x_x_rtp_conversion("octet-align=0", rtp_amr_5_15k_bwe, + "octet-align=1", ''O, /* We expect to see NO octet aligned packets! */ + "octet-align=0", rtp_amr_5_15k_bwe); } /* TODO: Double-DLCX (no retransmission) */ @@ -2207,7 +2575,7 @@ module MGCP_Test { f_init(ep); log("Setting conn-timeout to 1s"); - f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); /* reset in f_init_vty() */ + f_vty_config(MGWVTY, "mgcp", "conn-timeout 1"); log("Sending RTP data for 1.5s"); flow := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000/1")); @@ -2232,6 +2600,9 @@ module MGCP_Test { f_sleep(0.2); f_rtpem_conn_refuse_verify(RTPEM[0]); + log("Setting conn-timeout back to 0 (disabled)"); + f_vty_config(MGWVTY, "mgcp", "conn-timeout 0"); + setverdict(pass); } @@ -2317,6 +2688,69 @@ module MGCP_Test { setverdict(pass); } + /* test valid wildcarded DLCX on an E1 trunk */ + testcase TC_e1_dlcx_wildcarded() runs on dummy_CT { + var template MgcpCommand cmd; + var MgcpEndpoint ep; + var MgcpCallId call_id := '8376F297'H; + var integer n_e1_ts := 4; + var StatsDExpects expect; + + f_init(); + + /* Open a few E1 timeslots */ + for (var integer i := 0; i < n_e1_ts; i := i+1) { + ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-0@" & c_mgw_domain; + cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id); + mgcp_transceive_mgw(cmd, tr_CRCX_ACK); + ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-2@" & c_mgw_domain; + cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id); + mgcp_transceive_mgw(cmd, tr_CRCX_ACK); + ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-4@" & c_mgw_domain; + cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id); + mgcp_transceive_mgw(cmd, tr_CRCX_ACK); + ep := "ds/e1-1/s-" & int2str(i+1) & "/su16-6@" & c_mgw_domain; + cmd := ts_CRCX(get_next_trans_id(), ep, "recvonly", call_id); + mgcp_transceive_mgw(cmd, tr_CRCX_ACK); + } + + /* Wait until the stats items have settled and then check if we get the expected number (all) of + * occupied endpoints */ + f_sleep(1.0) + expect := { + { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := n_e1_ts * 4, max := n_e1_ts * 4} + }; + f_statsd_expect(expect); + + /* Send wildcarded DLCX */ + var template MgcpResponse rtmpl := { + line := { + code := "200", + string := ? + }, + params:= { }, + sdp := omit + }; + ep := "ds/e1-1/*@" & c_mgw_domain; + cmd := ts_DLCX(get_next_trans_id(), ep); + mgcp_transceive_mgw(cmd, rtmpl); + + /* Query the statsd once to ensure that intermediate results are pulled from the + * pipeline. The second query (below) will return the actual result. */ + expect := { + { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := n_e1_ts * 4} + }; + f_statsd_expect(expect); + + /* The second query must return a result with 0 endpoints in use. */ + expect := { + { name := "TTCN3.trunk.e1-1.common.endpoints.used", mtype := "g", min := 0, max := 0} + }; + f_statsd_expect(expect); + + setverdict(pass); + } + /* test valid CRCX then MDCX with IPv4 address, MGW provides a local IPv4 too */ testcase TC_crcx_mdcx_ip4() runs on dummy_CT { var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain; @@ -2383,8 +2817,523 @@ module MGCP_Test { setverdict(pass); } + /* test valid CRCX with IUFP in a given MGCP ConnectionMode ("sendrecv", "recvonly") */ + function f_tc_crcx_iufp_mode(charstring mgcp_mode) runs on dummy_CT { + var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain; + var template MgcpCommand cmd; + var MgcpResponse resp; + var MgcpCallId call_id := '1234'H; + var MgcpConnectionId conn_id; + + f_init(ep); + + /* create the connection on the MGW. + * Start directly in sendrecv mode, with IUFP and a (so far unknown) audio port == 0. + * The MGCP ConnectionMode 'M:' is mandatory in MGCP in a CRCX. Another a=sendrecv exists in SDP. */ + cmd := ts_CRCX(get_next_trans_id(), ep, mgcp_mode, call_id); + cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 0, { "96" }, + { valueof(ts_SDP_rtpmap(96, "VND.3GPP.IUFP/16000")), + valueof(ts_SDP_ptime(20)) }); + resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK); + conn_id := extract_conn_id(resp); + if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) { + setverdict(fail, "Wrong RemoteConnection in CRCX ACK", resp.sdp.connection); + } + + /* Add the audio port in a subsequent MDCX. */ + cmd := ts_MDCX(get_next_trans_id(), ep, "sendrecv", call_id, conn_id); + cmd.sdp := ts_SDP("127.0.0.2", "127.0.0.1", "23", "42", 2344, { "96" }, + { valueof(ts_SDP_rtpmap(96, "VND.3GPP.IUFP/16000")), + valueof(ts_SDP_ptime(20)) }); + resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK); + + if (not ispresent(resp.sdp) or not ispresent(resp.sdp.connection)) { + setverdict(fail, "No RemoteConnection info found in MDCX ACK"); + } + if (not match(resp.sdp.connection, ts_SDP_connection_IP("127.0.0.1", "IP4"))) { + setverdict(fail, "Wrong RemoteConnection in MDCX ACK", resp.sdp.connection); + } + + /* clean-up */ + f_dlcx_ok(ep, call_id); + setverdict(pass); + } + + testcase TC_crcx_iufp_sendrecv() runs on dummy_CT { + f_tc_crcx_iufp_mode("sendrecv"); + } + + testcase TC_crcx_iufp_recvonly() runs on dummy_CT { + f_tc_crcx_iufp_mode("recvonly"); + } + + /* create two local emulations and pass data in both directions */ + function f_two_crcx_mdcx_data_transfer(MgcpEndpoint ep, MgcpCallId call_id, inout RtpFlowData flow_a, + inout RtpFlowData flow_b, boolean tear_down_rtp := true) runs on dummy_CT { + var RtpemStats stats[2]; + var MgcpResponse resp; + var integer num_pkts_tx[2]; + var integer temp; + + /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */ + f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow_a, true); + f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY); + + /* Create the second connection. This connection will be also + * in receive only mode (CN side, regular RTP) */ + f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow_b, true); + f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY); + + /* The first leg starts transmitting */ + f_rtpem_mode(RTPEM[0], RTPEM_MODE_TXONLY); + f_sleep(0.5); + stats[0] := f_rtpem_stats_get(RTPEM[0]); + if (stats[0].num_pkts_rx_err_disabled != 0) { + setverdict(fail, "received packets from MGW on recvonly connection 0"); + mtc.stop; + } + stats[1] := f_rtpem_stats_get(RTPEM[1]); + if (stats[1].num_pkts_rx_err_disabled != 0) { + setverdict(fail, "received packets from MGW on recvonly connection 1"); + mtc.stop; + } + + /* The second leg starts transmitting a little later */ + f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY); + f_sleep(1.0); + stats[0] := f_rtpem_stats_get(RTPEM[0]); + if (stats[0].num_pkts_rx_err_disabled != 0) { + setverdict(fail, "received packets from MGW on recvonly connection 0"); + mtc.stop; + } + stats[1] := f_rtpem_stats_get(RTPEM[1]); + if (stats[1].num_pkts_rx_err_disabled != 0) { + setverdict(fail, "received packets from MGW on recvonly connection 1"); + mtc.stop; + } + + /* The first leg will now be switched into bidirectional + * mode, but we do not expect any data coming back yet. */ + f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR); + stats[1] := f_rtpem_stats_get(RTPEM[1]); + num_pkts_tx[1] := stats[1].num_pkts_tx; + f_flow_modify(RTPEM[0], ep, call_id, "sendrecv", flow_a); + f_sleep(0.5); + stats[0] := f_rtpem_stats_get(RTPEM[0]); + if (stats[0].num_pkts_rx_err_disabled != 0) { + setverdict(fail, "received packets from MGW on recvonly connection 0"); + mtc.stop; + } + stats[1] := f_rtpem_stats_get(RTPEM[1]); + if (stats[1].num_pkts_rx_err_disabled != 0) { + setverdict(fail, "received packets from MGW on recvonly connection 1"); + mtc.stop; + } + + /* When the second leg is switched into bidirectional mode + * as well, then the MGW will connect the two together and + * we should see RTP streams passing through from both ends. */ + f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR); + stats[0] := f_rtpem_stats_get(RTPEM[0]); + num_pkts_tx[0] := stats[0].num_pkts_tx; + f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow_b); + f_sleep(2.0); + + stats[0] := f_rtpem_stats_get(RTPEM[0]); + stats[1] := f_rtpem_stats_get(RTPEM[1]); + + temp := stats[0].num_pkts_tx - num_pkts_tx[0] - stats[1].num_pkts_rx; + if (temp > 3 or temp < -3) { + setverdict(fail, "number of packets not within normal parameters:", temp); + mtc.stop; + } + + temp := stats[1].num_pkts_tx - num_pkts_tx[1] - stats[0].num_pkts_rx; + if (temp > 3 or temp < -3) { + setverdict(fail, "number of packets not within normal parameters:", temp); + mtc.stop; + } + + f_rtpem_stats_err_check(stats[0]); + f_rtpem_stats_err_check(stats[1]); + + /* Tear down */ + if (tear_down_rtp) { + f_flow_delete(RTPEM[0]); + f_flow_delete(RTPEM[1], ep, call_id); + } + setverdict(pass); + } + + /* create two local RTP+IuUP emulations and pass data in both directions */ + function f_tc_two_crcx_mdcx_and_iuup(charstring local_ip_a, charstring remote_ip_a, + IuUP_RabFlowCombinationList rfcl_a, + charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT { + var RtpFlowData flow[2]; + var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain; + var MgcpCallId call_id := '1227'H; + + f_init(ep); + + /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */ + flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000")); + flow[0].em.portnr := 10000; + flow[0].rtp_cfg := c_RtpemDefaultCfg; + flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.iuup_mode := true; + flow[0].rtp_cfg.iuup_cfg.active_init := true; + flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a; + + /* Create the second connection. This connection will be also + * in receive only mode (CN side, IuUP-Init passive) */ + flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000")); + flow[1].em.portnr := 20000; + flow[1].rtp_cfg := c_RtpemDefaultCfg; + flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.iuup_mode := true; + flow[1].rtp_cfg.iuup_cfg.active_init := false; + + f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], true); + setverdict(pass); + } + testcase TC_two_crcx_mdcx_and_iuup() runs on dummy_CT { + var template (value) IuUP_RabFlowCombinationList rfcl := { + t_IuUP_RFC_AMR_12_2(0), + t_IuUP_RFC_AMR_SID(1), + t_IuUP_RFC_AMR_NO_DATA(2) + }; + f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl), + mp_local_ipv4, mp_remote_ipv4); + } + /* Same as TC_two_crcx_mdcx_and_iuup, but passing unordered RFCI list (ID != position) */ + testcase TC_two_crcx_mdcx_and_iuup_rfci_unordered() runs on dummy_CT { + var template (value) IuUP_RabFlowCombinationList rfcl := { + t_IuUP_RFC_AMR_12_2(1), + t_IuUP_RFC_AMR_SID(2), + t_IuUP_RFC_AMR_NO_DATA(0) + }; + f_tc_two_crcx_mdcx_and_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl), + mp_local_ipv4, mp_remote_ipv4); + } + + /* Test that once IuUP->RTP has been set up, if the RTP/IuUP conn is set + * as "recvonly", no more RTP/IuUP packets get out of the MGW. */ + function f_tc_two_crcx_mdcx_and_iuup_mdcx_recvonly(charstring local_ip_a, charstring remote_ip_a, + IuUP_RabFlowCombinationList rfcl_a, + charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT { + var RtpFlowData flow[2]; + var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain; + var MgcpCallId call_id := '1227'H; + var RtpemStats stats[2]; + + f_init(ep); + + /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */ + flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000")); + flow[0].em.portnr := 10000; + flow[0].rtp_cfg := c_RtpemDefaultCfg; + flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.iuup_mode := true; + flow[0].rtp_cfg.iuup_cfg.active_init := true; + flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a; + + /* Create the second connection. This connection will be also + * in receive only mode (CN side, IuUP-Init passive) */ + flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000")); + flow[1].em.portnr := 20000; + flow[1].rtp_cfg := c_RtpemDefaultCfg; + flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.iuup_mode := true; + flow[1].rtp_cfg.iuup_cfg.active_init := false; + + f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], false); + + /* Now validate we don't receive more RTP packets after setting it to recvonly: */ + f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY); + f_flow_modify(RTPEM[1], ep, call_id, "recvonly", flow[1]); + f_sleep(0.5); + stats[0] := f_rtpem_stats_get(RTPEM[1]); + f_sleep(0.5); + stats[1] := f_rtpem_stats_get(RTPEM[1]); + if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " RTP packets from MGW on recvonly connection 1")); + } + + /* Now do the same on the IuUP port: */ + f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY); + f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY); + f_flow_modify(RTPEM[0], ep, call_id, "recvonly", flow[0]); + f_sleep(0.5); + stats[0] := f_rtpem_stats_get(RTPEM[0]); + f_sleep(0.5); + stats[1] := f_rtpem_stats_get(RTPEM[0]); + if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " IuUP packets from MGW on recvonly connection 1")); + } + + /* Tear down */ + f_flow_delete(RTPEM[0]); + f_flow_delete(RTPEM[1], ep, call_id); + setverdict(pass); + } + testcase TC_two_crcx_mdcx_and_iuup_mdcx_recvonly() runs on dummy_CT { + var template (value) IuUP_RabFlowCombinationList rfcl := { + t_IuUP_RFC_AMR_12_2(0), + t_IuUP_RFC_AMR_SID(1), + t_IuUP_RFC_AMR_NO_DATA(2) + }; + f_tc_two_crcx_mdcx_and_iuup_mdcx_recvonly(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl), + mp_local_ipv4, mp_remote_ipv4); + } + + /* create two local emulations (1 RTP, 1 RTP+IuUP) and pass data in both directions */ + function f_tc_two_crcx_mdcx_and_iuup_rtp(charstring local_ip_a, charstring remote_ip_a, + IuUP_RabFlowCombinationList rfcl_a, + charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT { + var RtpFlowData flow[2]; + var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain; + var MgcpCallId call_id := '1227'H; + + f_init(ep); + + /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */ + flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000")); + flow[0].em.portnr := 10000; + flow[0].rtp_cfg := c_RtpemDefaultCfg; + flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O; + /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/ + flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O; + flow[0].rtp_cfg.iuup_mode := true; + flow[0].rtp_cfg.iuup_cfg.active_init := true; + flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a; + + /* Create the second connection. This connection will be also + * in receive only mode (CN side, regular RTP) */ + flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000")); + flow[1].em.portnr := 20000; + flow[1].rtp_cfg := c_RtpemDefaultCfg; + flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O; + /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/ + flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O; + flow[1].rtp_cfg.iuup_mode := false; + + f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], true); + setverdict(pass); + } + testcase TC_two_crcx_mdcx_and_iuup_rtp() runs on dummy_CT { + var template (value) IuUP_RabFlowCombinationList rfcl := { + t_IuUP_RFC_AMR_12_2(0), + t_IuUP_RFC_AMR_SID(1), + t_IuUP_RFC_AMR_NO_DATA(2) + }; + f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl), + mp_local_ipv4, mp_remote_ipv4); + } + /* Same as TC_two_crcx_mdcTC_two_crcx_mdcx_and_iuup_rtpx_and_iuup, but passing unordered RFCI list (ID != position) */ + testcase TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered() runs on dummy_CT { + var template (value) IuUP_RabFlowCombinationList rfcl := { + t_IuUP_RFC_AMR_12_2(1), + t_IuUP_RFC_AMR_SID(2), + t_IuUP_RFC_AMR_NO_DATA(0) + }; + f_tc_two_crcx_mdcx_and_iuup_rtp(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl), + mp_local_ipv4, mp_remote_ipv4); + } + + /* Test that once IuUP->RTP has been set up, if the RTP/IuUP conn is set + * as "recvonly", no more RTP/IuUP packets get out of the MGW. */ + function f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly(charstring local_ip_a, charstring remote_ip_a, + IuUP_RabFlowCombinationList rfcl_a, + charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT { + var RtpFlowData flow[2]; + var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain; + var MgcpCallId call_id := '1227'H; + var RtpemStats stats[2]; + + f_init(ep); + + /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */ + flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000")); + flow[0].em.portnr := 10000; + flow[0].rtp_cfg := c_RtpemDefaultCfg; + flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O; + /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/ + flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O; + flow[0].rtp_cfg.iuup_mode := true; + flow[0].rtp_cfg.iuup_cfg.active_init := true; + flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a; + + /* Create the second connection. This connection will be also + * in receive only mode (CN side, regular RTP) */ + flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000")); + flow[1].em.portnr := 20000; + flow[1].rtp_cfg := c_RtpemDefaultCfg; + flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O; + /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/ + flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O; + flow[1].rtp_cfg.iuup_mode := false; + + f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], false); + + /* Now validate we don't receive more RTP packets after setting it to recvonly: */ + f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY); + f_flow_modify(RTPEM[1], ep, call_id, "recvonly", flow[1]); + f_sleep(0.5); + stats[0] := f_rtpem_stats_get(RTPEM[1]); + f_sleep(0.5); + stats[1] := f_rtpem_stats_get(RTPEM[1]); + if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " RTP packets from MGW on recvonly connection 1")); + } + + /* Now do the same on the IuUP port: */ + f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY); + f_flow_modify(RTPEM[0], ep, call_id, "recvonly", flow[0]); + f_rtpem_mode(RTPEM[1], RTPEM_MODE_TXONLY); + f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]); + f_sleep(0.5); + stats[0] := f_rtpem_stats_get(RTPEM[0]); + f_sleep(0.5); + stats[1] := f_rtpem_stats_get(RTPEM[0]); + if (stats[1].num_pkts_rx > stats[0].num_pkts_rx) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("received ", stats[1].num_pkts_rx - stats[0].num_pkts_rx, " IuUP packets from MGW on recvonly connection 1")); + } + + /* Tear down */ + f_flow_delete(RTPEM[0]); + f_flow_delete(RTPEM[1], ep, call_id); + setverdict(pass); + } + testcase TC_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly() runs on dummy_CT { + var template (value) IuUP_RabFlowCombinationList rfcl := { + t_IuUP_RFC_AMR_12_2(0), + t_IuUP_RFC_AMR_SID(1), + t_IuUP_RFC_AMR_NO_DATA(2) + }; + f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl), + mp_local_ipv4, mp_remote_ipv4); + } + + /* Set up Endpoint with 1 IuUP conn and 1 RTP-AMR conn, then MDCX the later to become IuUP. */ + function f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup(charstring local_ip_a, charstring remote_ip_a, + IuUP_RabFlowCombinationList rfcl_a, + charstring local_ip_b, charstring remote_ip_b) runs on dummy_CT { + var RtpFlowData flow[2]; + var MgcpEndpoint ep := c_mgw_ep_rtpbridge & "2@" & c_mgw_domain; + var MgcpCallId call_id := '1227'H; + var RtpemStats stats[2][2]; + + f_init(ep); + + /* Create the first connection in receive only mode (RNC side, IuUP-Init active) */ + flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000")); + flow[0].em.portnr := 10000; + flow[0].rtp_cfg := c_RtpemDefaultCfg; + flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt; + flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O; + /* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/ + flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O; + flow[0].rtp_cfg.iuup_mode := true; + flow[0].rtp_cfg.iuup_cfg.active_init := true; + flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a; + + /* Create the second connection. This connection will be also + * in receive only mode (CN side, regular RTP) */ + flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000")); + flow[1].em.portnr := 20000; + flow[1].rtp_cfg := c_RtpemDefaultCfg; + flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O; + /* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/ + flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O; + flow[1].rtp_cfg.iuup_mode := false; + + f_two_crcx_mdcx_data_transfer(ep, call_id, flow[0], flow[1], false); + setverdict(pass); + + /* Stop sending further pkts to RTPEM[1], to avoid race conditions where RTP is sent while we + * reconfigure MGW (and our RTPEM) for IuUP: + */ + f_rtpem_mode(RTPEM[0], RTPEM_MODE_RXONLY); + f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY); + f_sleep(0.5); + + /* Modify the AMR side to also do IuUP */ + flow[1].codec_descr := {{ + pt := 96, + codec := "VND.3GPP.IUFP/16000", + fmtp := omit + }}; + flow[1].rtp_cfg.iuup_mode := true; + flow[1].rtp_cfg.iuup_cfg.active_init := false; + flow[1].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a; + flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + /* Send whatever payload is expected by the other RTPEM, now without AMR-BE_RTP<->AMR-IUUP conversion: */ + flow[1].rtp_cfg.tx_payloads[0].fixed_payload := flow[0].rtp_cfg.rx_payloads[0].fixed_payload; + flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt; + /* Expect whatever payload is sent by the other RTPEM, now without AMR-BE_RTP<->AMR-IUUP conversion: */ + flow[1].rtp_cfg.rx_payloads[0].fixed_payload := flow[0].rtp_cfg.tx_payloads[0].fixed_payload; + f_flow_modify(RTPEM[1], ep, call_id, "sendrecv", flow[1]); + f_rtpem_mode(RTPEM[0], RTPEM_MODE_BIDIR); + f_sleep(1.0); + f_rtpem_mode(RTPEM[1], RTPEM_MODE_BIDIR); + + /* Now, if MGW would keep sending RTP instead of expected IuUP, then that would make the RTPEM[1] + * component to fail, since it would error trying to decode an RTP pkt as IuUP. + * We simply need to make sure some packets are being forwarded to it: + */ + stats[0][0] := f_rtpem_stats_get(RTPEM[0]); + stats[1][0] := f_rtpem_stats_get(RTPEM[1]); + f_sleep(1.0); + stats[0][1] := f_rtpem_stats_get(RTPEM[0]); + stats[1][1] := f_rtpem_stats_get(RTPEM[1]); + + f_rtpem_stats_err_check(stats[0][1]); + f_rtpem_stats_err_check(stats[1][1]); + if (stats[0][1].num_pkts_rx == stats[0][0].num_pkts_rx) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("No packets received in connection 0 after MDCX")); + } + if (stats[1][1].num_pkts_rx == stats[1][0].num_pkts_rx) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str("No packets received in connection 1 after MDCX")); + } + + /* Tear down */ + f_flow_delete(RTPEM[0]); + f_flow_delete(RTPEM[1], ep, call_id); + setverdict(pass); + } + testcase TC_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup() runs on dummy_CT { + var template (value) IuUP_RabFlowCombinationList rfcl := { + t_IuUP_RFC_AMR_12_2(0), + t_IuUP_RFC_AMR_SID(1), + t_IuUP_RFC_AMR_NO_DATA(2) + }; + f_tc_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup(mp_local_ipv4, mp_remote_ipv4, valueof(rfcl), + mp_local_ipv4, mp_remote_ipv4); + } control { execute(TC_selftest()); + execute(TC_auep_null()); execute(TC_crcx()); execute(TC_crcx_no_lco()); execute(TC_crcx_noprefix()); @@ -2401,6 +3350,7 @@ module MGCP_Test { execute(TC_crcx_wildcarded_exhaust()); execute(TC_mdcx_without_crcx()); execute(TC_dlcx_without_crcx()); + execute(TC_dlcx_non_existant_ep()); execute(TC_mdcx_wildcarded()); execute(TC_dlcx_wildcarded()); execute(TC_crcx_and_dlcx_ep_callid_connid()); @@ -2413,10 +3363,12 @@ module MGCP_Test { execute(TC_crcx_osmux_wildcard()); execute(TC_crcx_osmux_fixed()); execute(TC_crcx_osmux_fixed_twice()); + execute(TC_crcx_osmux_257()); execute(TC_one_crcx_receive_only_osmux()); execute(TC_one_crcx_loopback_osmux()); execute(TC_two_crcx_and_rtp_osmux()); execute(TC_two_crcx_and_rtp_osmux_bidir()); + execute(TC_two_crcx_and_rtp_osmux_bidir_amr_bwe()); execute(TC_two_crcx_mdcx_and_rtp_osmux_wildcard()); execute(TC_two_crcx_mdcx_and_rtp_osmux_fixed()); @@ -2426,6 +3378,7 @@ module MGCP_Test { execute(TC_one_crcx_receive_only_rtp()); execute(TC_one_crcx_loopback_rtp()); + execute(TC_one_crcx_loopback_rtp_ipv6()); execute(TC_two_crcx_and_rtp()); execute(TC_two_crcx_and_rtp_bidir()); execute(TC_two_crcx_diff_pt_and_rtp()); @@ -2433,19 +3386,28 @@ module MGCP_Test { execute(TC_two_crcx_mdcx_and_rtp()); execute(TC_two_crcx_and_unsolicited_rtp()); execute(TC_two_crcx_and_one_mdcx_rtp_ho()); + execute(TC_two_crcx_confecho_sendonly_rtp()); execute(TC_ts101318_rfc5993_rtp_conversion()); + execute(TC_ts101318_rfc5993_rtp_conversion_fmtp()); execute(TC_amr_oa_bwe_rtp_conversion()); execute(TC_amr_oa_oa_rtp_conversion()); execute(TC_amr_bwe_bwe_rtp_conversion()); + execute(TC_amr_oa_oa_no_bwe_rtp_conversion()); + execute(TC_amr_oa_no_bwe_oa_rtp_conversion()); + execute(TC_amr_bwe_bwe_no_oa_rtp_conversion()); + execute(TC_amr_bwe_no_oa_bwe_rtp_conversion()); execute(TC_conn_timeout()); execute(TC_e1_crcx_and_dlcx_ep()); execute(TC_e1_crcx_with_overlap()); execute(TC_e1_crcx_loopback()); + execute(TC_e1_dlcx_wildcarded()); execute(TC_crcx_mdcx_ip4()); execute(TC_crcx_mdcx_ip6()); + execute(TC_crcx_iufp_sendrecv()); + execute(TC_crcx_iufp_recvonly()); execute(TC_two_crcx_mdcx_and_rtp_ipv4_ipv6()); execute(TC_two_crcx_mdcx_and_rtp_ipv6()); execute(TC_two_crcx_and_rtp_osmux_bidir_ipv6()); @@ -2454,5 +3416,21 @@ module MGCP_Test { execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6()); execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv4_ipv6()); execute(TC_two_crcx_mdcx_and_rtp_osmux_ipv6_ipv4()); + + execute(TC_two_crcx_mdcx_and_iuup()); + execute(TC_two_crcx_mdcx_and_iuup_rfci_unordered()); + execute(TC_two_crcx_mdcx_and_iuup_mdcx_recvonly()); + execute(TC_two_crcx_mdcx_and_iuup_rtp()); + execute(TC_two_crcx_mdcx_and_iuup_rtp_rfci_unordered()); + execute(TC_two_crcx_mdcx_and_iuup_rtp_mdcx_recvonly()); + execute(TC_two_crcx_mdcx_and_iuup_rtp_mdcx_to_iuup()); + + execute(TC_two_crcx_mdcx_and_rtp_clearmode()); + + /* Note: This testcase will trigger an OSMO_ASSERT() bug in + * older versions of osmo-mgw. This eventually leads into + * a failure of all subsequent testcases, so it is important + * not to add new testcaes after this one. */ + execute(TC_one_crcx_loopback_rtp_implicit()); } } |