pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/37426?usp=email )
Change subject: asterisk: Introduce test TC_ims_call_mo_holdresume_mo ......................................................................
asterisk: Introduce test TC_ims_call_mo_holdresume_mo
Related: SYS#7002 Change-Id: Ifffa1c4021f324871f11a60264c17b640569e18b --- M asterisk/Asterisk_Tests.ttcn M asterisk/IMS_ConnectionHandler.ttcn M asterisk/SIP_ConnectionHandler.ttcn M asterisk/expected-results.xml M library/SDP_Templates.ttcn 5 files changed, 187 insertions(+), 24 deletions(-)
Approvals: pespin: Looks good to me, approved Jenkins Builder: Verified laforge: Looks good to me, but someone else must approve
diff --git a/asterisk/Asterisk_Tests.ttcn b/asterisk/Asterisk_Tests.ttcn index ec55254..0796d99 100644 --- a/asterisk/Asterisk_Tests.ttcn +++ b/asterisk/Asterisk_Tests.ttcn @@ -226,6 +226,16 @@ f_TC_internal_hangup_call_mo_unregister(); setverdict(pass); } +private function f_TC_internal_call_mo_with_holdresume(charstring id) runs on SIPConnHdlr { + f_TC_internal_register_establish_call_mo(); + f_sleep(1.0); + f_SIP_do_call_hold(); + f_sleep(1.0); + f_SIP_do_call_resume(); + f_TC_internal_hangup_call_mo_unregister(); + setverdict(pass); +} + private function f_TC_internal_call_mt(charstring id) runs on SIPConnHdlr {
f_create_sip_expect(valueof(ts_SipUrl_from_Addr_Union(g_pars.cp.called.addr))); @@ -756,6 +766,14 @@ f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister(); }
+private function f_TC_ims_call_mo_IMS_ConnHdlr_with_holdresume(charstring id) runs on IMS_ConnHdlr { + f_TC_ims_call_mo_IMS_ConnHdlr_register_establish_call(); + as_IMS_exp_call_hold(); + f_sleep(1.0); + as_IMS_exp_call_resume(); + f_TC_ims_call_mo_IMS_ConnHdlr_hangup_call_unregister(); +} + private function f_TC_ims_call_mo(boolean use_precondition_ext := true, boolean use_session_timer := false, void_fn sip_fn := refers(f_TC_internal_call_mo), ims_void_fn ims_fn := refers(f_TC_ims_call_mo_IMS_ConnHdlr)) runs on test_CT { @@ -825,6 +843,10 @@ testcase TC_ims_call_mo_noprecondition() runs on test_CT { f_TC_ims_call_mo(use_precondition_ext := false); } +testcase TC_ims_call_mo_holdresume_mo() runs on test_CT { + f_TC_ims_call_mo(sip_fn := refers(f_TC_internal_call_mo_with_holdresume), + ims_fn := refers(f_TC_ims_call_mo_IMS_ConnHdlr_with_holdresume)); +}
/* Test SIP registration of local clients */ private function f_TC_ims_call_mt_IMS_ConnHdlr(charstring id) runs on IMS_ConnHdlr { @@ -942,6 +964,7 @@ execute( TC_ims_call_mo() ); execute( TC_ims_call_mo_session_timer() ); execute( TC_ims_call_mo_noprecondition() ); + execute( TC_ims_call_mo_holdresume_mo() ); execute( TC_ims_call_mt() ); execute( TC_ims_call_mt_noprecondition() ); } diff --git a/asterisk/IMS_ConnectionHandler.ttcn b/asterisk/IMS_ConnectionHandler.ttcn index dd187ad..14e6cd4 100644 --- a/asterisk/IMS_ConnectionHandler.ttcn +++ b/asterisk/IMS_ConnectionHandler.ttcn @@ -650,7 +650,7 @@ IMS_GEN_SDP_MT_UPDATE_200OK }
-private function f_gen_sdp(IMS_gen_sdp_state st := IMS_GEN_SDP_None) runs on IMS_ConnHdlr return charstring { +private function f_gen_sdp(IMS_gen_sdp_state st := IMS_GEN_SDP_None, charstring dir := "sendrecv") runs on IMS_ConnHdlr return charstring { var charstring sdp := "v=0\r\n" & "o=0502 2390 1824 IN IP4 " & g_pars.subscr.cp.local_rtp_addr & "\r\n" & @@ -659,15 +659,8 @@ "t=0 0\r\n" & "a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics\r\n" & "a=record:off\r\n" & - "m=audio " & int2str(g_pars.subscr.cp.local_rtp_port) & " RTP/AVP 8 96 97 98 0 18 99 100 101\r\n" & + "m=audio " & int2str(g_pars.subscr.cp.local_rtp_port) & " RTP/AVP 8 99 100 101\r\n" & "a=rtpmap:8 PCMA/8000\r\n" & - "a=rtpmap:96 opus/48000/2\r\n" & - "a=fmtp:96 useinbandfec=1\r\n" & - "a=rtpmap:97 speex/16000\r\n" & - "a=fmtp:97 vbr=on\r\n" & - "a=rtpmap:98 speex/8000\r\n" & - "a=fmtp:98 vbr=on\r\n" & - "a=fmtp:18 annexb=yes\r\n" & "a=rtpmap:99 telephone-event/48000\r\n" & "a=rtpmap:100 telephone-event/16000\r\n" & "a=rtpmap:101 telephone-event/8000\r\n" & @@ -707,6 +700,8 @@ "a=des:qos mandatory remote sendrecv\r\n"; } } + + sdp := sdp & "a=" & dir & "\r\n"; return sdp; }
@@ -1708,10 +1703,10 @@ tx_resp := ts_SIP_Response(g_pars.subscr.cp.sip_call_id, g_pars.subscr.cp.from_addr, g_pars.subscr.cp.to_addr, - g_rx_sip_req.msgHeader.cSeq.method, 200, - g_rx_sip_req.msgHeader.cSeq.seqNumber, + req.msgHeader.cSeq.method, 200, + req.msgHeader.cSeq.seqNumber, "OK", - g_rx_sip_req.msgHeader.via, + req.msgHeader.via, session_expires := ts_Session_expires(int2str(g_pars.subscr.cp.mo.support_timer_session_expires), {ts_Param("refresher", "uac")}), supported := ts_Supported({"timer"}), @@ -1750,4 +1745,66 @@ g_pars.subscr.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber; } } + +private altstep as_IMS_exp_call_holdresume(template (present) SDP_attribute exp_dir := tr_SDP_sendonly, + charstring tx_dir := "recvonly") runs on IMS_ConnHdlr +{ + /* RFC4028 section 7.4. It can be either INVITE or UPDATE: */ + var template (present) PDU_SIP_Request exp_req_invite := + tr_SIP_INVITE(f_tr_SipUrl_opt_defport(ts_SipUrl_from_Addr_Union(g_pars.subscr.cp.called.addr)), + ?, + tr_From(tr_Addr_Union_from_val(g_pars.subscr.cp.calling.addr), *), + tr_To(tr_Addr_Union_from_val(g_pars.subscr.cp.called.addr), *), + tr_Via_from(f_tr_HostPort(g_pars.subscr.remote_sip_host, g_pars.subscr.ipsec_remote_port_s)), + ?, + body := ?); + + [] SIP.receive(exp_req_invite) -> value g_rx_sip_req { + var template (present) PDU_SIP_Request exp_req; + var template (value) PDU_SIP_Response tx_resp; + var charstring tx_sdp; + + f_SDP_decodeMessage(g_rx_sip_req.messageBody, g_pars.subscr.cp.peer_sdp); + g_pars.subscr.cp.sip_seq_nr := g_rx_sip_req.msgHeader.cSeq.seqNumber; + + /* Check a=$exp_dir in SDP */ + if (not ispresent(g_pars.subscr.cp.peer_sdp.media_list[0].attributes) or + not match (g_pars.subscr.cp.peer_sdp.media_list[0].attributes, superset(exp_dir))) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str(g_name & ": Expected re-INVITE SDP attribute a=", exp_dir, " in: ", + g_pars.subscr.cp.peer_sdp.media_list[0].attributes)); + } + + tx_sdp := f_gen_sdp(dir := tx_dir); + /* Tx 200 OK response*/ + tx_resp := ts_SIP_Response(g_pars.subscr.cp.sip_call_id, + g_pars.subscr.cp.from_addr, + g_pars.subscr.cp.to_addr, + g_rx_sip_req.msgHeader.cSeq.method, 200, + g_rx_sip_req.msgHeader.cSeq.seqNumber, + "OK", + g_rx_sip_req.msgHeader.via, + body := tx_sdp); + SIP.send(tx_resp); + + /* Wait for ACK */ + exp_req := tr_SIP_ACK(f_tr_SipUrl_opt_defport(ts_SipUrl_from_Addr_Union(g_pars.subscr.cp.called.addr)), + g_pars.subscr.cp.sip_call_id, + g_pars.subscr.cp.from_addr, + g_pars.subscr.cp.to_addr, + f_tr_Via_response(g_rx_sip_req.msgHeader.via), + g_rx_sip_req.msgHeader.cSeq.seqNumber, + body := omit); + as_SIP_expect_req(exp_req); + } +} +altstep as_IMS_exp_call_hold() runs on IMS_ConnHdlr +{ + [] as_IMS_exp_call_holdresume(tr_SDP_sendonly, "recvonly"); +} +altstep as_IMS_exp_call_resume() runs on IMS_ConnHdlr +{ + [] as_IMS_exp_call_holdresume(tr_SDP_sendrecv, "sendrecv"); +} + } diff --git a/asterisk/SIP_ConnectionHandler.ttcn b/asterisk/SIP_ConnectionHandler.ttcn index f688fe0..ad76296 100644 --- a/asterisk/SIP_ConnectionHandler.ttcn +++ b/asterisk/SIP_ConnectionHandler.ttcn @@ -239,7 +239,7 @@ } }
-private function f_gen_sdp() runs on SIPConnHdlr return charstring { +private function f_gen_sdp(charstring dir := "sendrecv") runs on SIPConnHdlr return charstring { var charstring sdp := "v=0\r\n" & "o=0502 2390 1824 IN IP4 " & g_pars.cp.local_rtp_addr & "\r\n" & @@ -248,18 +248,9 @@ "t=0 0\r\n" & "a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics\r\n" & "a=record:off\r\n" & - "m=audio " & int2str(g_pars.cp.local_rtp_port) & " RTP/AVP 8 96 97 98 0 18 99 100 101\r\n" & + "m=audio " & int2str(g_pars.cp.local_rtp_port) & " RTP/AVP 8\r\n" & + "a=" & dir & "\r\n" & "a=rtpmap:8 PCMA/8000\r\n" & - "a=rtpmap:96 opus/48000/2\r\n" & - "a=fmtp:96 useinbandfec=1\r\n" & - "a=rtpmap:97 speex/16000\r\n" & - "a=fmtp:97 vbr=on\r\n" & - "a=rtpmap:98 speex/8000\r\n" & - "a=fmtp:98 vbr=on\r\n" & - "a=fmtp:18 annexb=yes\r\n" & - "a=rtpmap:99 telephone-event/48000\r\n" & - "a=rtpmap:100 telephone-event/16000\r\n" & - "a=rtpmap:101 telephone-event/8000\r\n" & "a=rtcp:" & int2str(g_pars.cp.local_rtp_port + 1) & "\r\n" & "a=rtcp-fb:* trr-int 1000\r\n" & "a=rtcp-fb:* ccm tmmbr\r\n"; @@ -815,4 +806,81 @@ [fail_others] as_SIP_fail_req(sip_expect_str); }
+/* initiate HOLD Tx RE-INVITE (3GPP TS 24.610 A.1.1): */ +private function f_SIP_do_call_holdresume(charstring dir := "sendonly", + template (present) SDP_attribute exp_resp_dir := tr_SDP_recvonly) runs on SIPConnHdlr +{ + var template (value) PDU_SIP_Request req; + var template (present) PDU_SIP_Response exp_resp; + var template (present) From from_addr_exp; + var template (present) To to_addr_exp; + var Via via; + var charstring branch_value; + var charstring tx_sdp := f_gen_sdp(dir := dir); + + /* Reuse g_pars.cp.from_addr and g_pars.cp.to_addr from established called */ + from_addr_exp := tr_From(tr_Addr_Union_from_val(g_pars.cp.from_addr.addressField), *); + to_addr_exp := tr_To(tr_Addr_Union_from_val(g_pars.cp.to_addr.addressField), *); + branch_value := f_sip_gen_branch(f_sip_Addr_Union_to_str(g_pars.cp.from_addr.addressField), + f_sip_Addr_Union_to_str(valueof(g_pars.cp.to_addr.addressField)), + g_pars.cp.sip_call_id, + g_pars.cp.sip_seq_nr); + + via := g_pars.local_via; + via.viaBody[0].viaParams := f_sip_param_set(via.viaBody[0].viaParams, "branch", branch_value); + + /* Transmit re-INVITE (HOLD) */ + req := ts_SIP_INVITE(g_pars.cp.sip_call_id, + g_pars.cp.from_addr, + g_pars.cp.to_addr, + via, + g_pars.local_contact, + g_pars.cp.sip_seq_nr, + body := tx_sdp); + SIP.send(req); + + /* Wait for OK answer */ + exp_resp := tr_SIP_Response( + g_pars.cp.sip_call_id, + from_addr_exp, + to_addr_exp, + f_tr_Via_response(via), + *, + "INVITE", 200, + g_pars.cp.sip_seq_nr, "OK", + body := ?); + as_SIP_expect_resp(exp_resp); + f_SDP_decodeMessage(g_rx_sip_resp.messageBody, g_pars.cp.peer_sdp); + + /* Check a=$exp_resp_dir in SDP */ + if (not ispresent(g_pars.cp.peer_sdp.media_list[0].attributes) or + not match (g_pars.cp.peer_sdp.media_list[0].attributes, superset(exp_resp_dir))) { + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, + log2str(g_name & ": Expected 200 OK (re-INVITE) SDP attribute a=", exp_resp_dir, " in: ", + g_pars.cp.peer_sdp.media_list[0].attributes)); + } + + /* Update To with the tags received from peer: */ + g_pars.cp.to_addr := g_rx_sip_resp.msgHeader.toField; + + /* Transmit ACK */ + req := ts_SIP_ACK(g_pars.cp.to_addr.addressField.nameAddr.addrSpec, + g_pars.cp.sip_call_id, + g_pars.cp.from_addr, + g_pars.cp.to_addr, + via, + g_pars.cp.sip_seq_nr, + omit); + SIP.send(req); + g_pars.cp.sip_seq_nr := g_pars.cp.sip_seq_nr + 1; +} +function f_SIP_do_call_hold() runs on SIPConnHdlr +{ + f_SIP_do_call_holdresume(dir := "sendonly", exp_resp_dir := tr_SDP_recvonly); +} +function f_SIP_do_call_resume() runs on SIPConnHdlr +{ + f_SIP_do_call_holdresume(dir := "sendrecv", exp_resp_dir := tr_SDP_sendrecv); +} + } diff --git a/asterisk/expected-results.xml b/asterisk/expected-results.xml index 179ee70..1819072 100644 --- a/asterisk/expected-results.xml +++ b/asterisk/expected-results.xml @@ -17,6 +17,7 @@ <testcase classname='Asterisk_Tests' name='TC_ims_call_mo' time='MASKED'/> <testcase classname='Asterisk_Tests' name='TC_ims_call_mo_session_timer' time='MASKED'/> <testcase classname='Asterisk_Tests' name='TC_ims_call_mo_noprecondition' time='MASKED'/> + <testcase classname='Asterisk_Tests' name='TC_ims_call_mo_holdresume_mo' time='MASKED'/> <testcase classname='Asterisk_Tests' name='TC_ims_call_mt' time='MASKED'/> <testcase classname='Asterisk_Tests' name='TC_ims_call_mt_noprecondition' time='MASKED'/> </testsuite> diff --git a/library/SDP_Templates.ttcn b/library/SDP_Templates.ttcn index 782c672..b8fc6c8 100644 --- a/library/SDP_Templates.ttcn +++ b/library/SDP_Templates.ttcn @@ -155,6 +155,10 @@ sendonly := {} }
+template (present) SDP_attribute tr_SDP_sendrecv := { + sendrecv := {} +} + /* rfc3312 */ const charstring c_SDP_PRECON_TYPE_qos := "qos"; const charstring c_SDP_PRECON_STRENGTH_TAG_mandatory := "mandatory";