pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/36490?usp=email )
Change subject: Introduce Asterisk_Tests testsuite ...................................................................... Introduce Asterisk_Tests testsuite Add initial infrastructure to run tests against an Asterisk process. An not-yet-finished draft test doing registration is submitted to validate communication towards Asterisk works. The testsuite will be improved in follow-up commits, but this way other people can already start using it and we can set up the dockerized setup + jenkins jobs to run it nightly. Related: SYS#6782 Change-Id: I66f776d5df6fb5dc488d9e589b84a6b2385406e8 --- M Makefile A asterisk/Asterisk_Tests.cfg A asterisk/Asterisk_Tests.default A asterisk/Asterisk_Tests.ttcn A asterisk/README.md A asterisk/expected-results.xml A asterisk/gen_links.sh A asterisk/regen_makefile.sh M library/SIP_Emulation.ttcn M library/SIP_Templates.ttcn 10 files changed, 414 insertions(+), 1 deletion(-) git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/90/36490/1 diff --git a/Makefile b/Makefile index 51e51ed..bd88ca5 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ # limitations under the License. SUBDIRS= \ + asterisk \ bsc \ bsc-nat \ bts \ diff --git a/asterisk/Asterisk_Tests.cfg b/asterisk/Asterisk_Tests.cfg new file mode 100644 index 0000000..a3669f9 --- /dev/null +++ b/asterisk/Asterisk_Tests.cfg @@ -0,0 +1,18 @@ +[ORDERED_INCLUDE] +# Common configuration, shared between test suites +"../Common.cfg" +# testsuite specific configuration, not expected to change +"./Asterisk_Tests.default" + +# Local configuration below + +[LOGGING] + +[TESTPORT_PARAMETERS] + +[MODULE_PARAMETERS] + +[MAIN_CONTROLLER] + +[EXECUTE] +Asterisk_Tests.control diff --git a/asterisk/Asterisk_Tests.default b/asterisk/Asterisk_Tests.default new file mode 100644 index 0000000..a2fda0f --- /dev/null +++ b/asterisk/Asterisk_Tests.default @@ -0,0 +1,18 @@ +[LOGGING] +FileMask := LOG_ALL | TTCN_MATCHING; + +mtc.FileMask := ERROR | WARNING | PARALLEL | VERDICTOP; + +[TESTPORT_PARAMETERS] +*.SIP.local_sip_port := "5060" +*.SIP.default_local_address := "127.0.0.2" +*.SIP.default_sip_protocol := "UDP" +*.SIP.default_dest_port := "5060" +*.SIP.default_dest_address := "127.0.0.1" + + +[MODULE_PARAMETERS] + +[MAIN_CONTROLLER] + +[EXECUTE] diff --git a/asterisk/Asterisk_Tests.ttcn b/asterisk/Asterisk_Tests.ttcn new file mode 100644 index 0000000..5d9754e --- /dev/null +++ b/asterisk/Asterisk_Tests.ttcn @@ -0,0 +1,240 @@ +module Asterisk_Tests { + +/* Asterisk test suite in TTCN-3 + * (C) 2024 by sysmocom - s.f.m.c. GmbH <i...@sysmocom.de> + * All rights reserved. + * Author: Pau Espin Pedrol <pes...@sysmocom.de> + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import from General_Types all; +import from Osmocom_Types all; +import from Native_Functions all; +import from Misc_Helpers all; + +import from SDP_Types all; +import from SDP_Templates all; + +import from SIP_Emulation all; +import from SIPmsg_Types all; +import from SIP_Templates all; + +modulepar { + charstring mp_local_sip_host := "127.0.0.2"; + integer mp_local_sip_port := 5060; + charstring mp_remote_sip_host := "127.0.0.1"; + integer mp_remote_sip_port := 5060; +} + +type component test_CT { + var SIP_Emulation_CT vc_SIP; +} + +type component ConnHdlr extends SIP_ConnHdlr { + var ConnHdlrPars g_pars; + timer g_Tguard; + var PDU_SIP_Request g_rx_sip_req; + var PDU_SIP_Response g_rx_sip_resp; +} + +type record ConnHdlrPars { + float t_guard, + charstring user, + SipUrl registrar_sip_url, + SipAddr registrar_sip_record, + CallidString registrar_sip_call_id, + Via registrar_via, + integer registrar_sip_seq_nr, + SipAddr sip_url_ext, + Contact local_contact, + CallPars cp optional +} + +template (value) ConnHdlrPars t_Pars(charstring user, + charstring displayname := "\"Anonymous\"") := { + t_guard := 30.0, + user := user, + registrar_sip_url := valueof(ts_SipUrlHost(mp_remote_sip_host)), + registrar_sip_record := ts_SipAddr(ts_HostPort(mp_remote_sip_host), + ts_UserInfo(user), + displayName := displayname), + registrar_sip_call_id := hex2str(f_rnd_hexstring(15)) & "@" & mp_local_sip_host, + registrar_via := ts_Via_from(ts_HostPort(mp_local_sip_host, mp_local_sip_port)), + registrar_sip_seq_nr := f_sip_rand_seq_nr(), + sip_url_ext := ts_SipAddr(ts_HostPort(mp_local_sip_host, mp_local_sip_port), + ts_UserInfo(user)), + local_contact := valueof(ts_Contact({ + ts_ContactAddress( + ts_Addr_Union_SipUrl(ts_SipUrl(ts_HostPort( + mp_local_sip_host, + mp_local_sip_port), + ts_UserInfo(user))), + omit) + })), + cp := omit +} + +function f_init_ConnHdlrPars(integer idx := 1) runs on test_CT return ConnHdlrPars { + var ConnHdlrPars pars := valueof(t_Pars(int2str(500 + idx))); + return pars; +} + +type record CallPars { + boolean is_mo, + charstring calling, + charstring called, + + CallParsComputed comp optional, + + charstring sip_rtp_addr, + uint16_t sip_rtp_port, + charstring cn_rtp_addr, + uint16_t cn_rtp_port +} + +type record CallParsComputed { + CallidString sip_call_id, + charstring sip_body, + integer sip_seq_nr +} + +private template (value) CallPars t_CallPars(boolean is_mo) := { + is_mo := is_mo, + calling := "12345", + called := "98766", + comp := { + sip_call_id := hex2str(f_rnd_hexstring(15)), + sip_body := "", + sip_seq_nr := f_sip_rand_seq_nr() + }, + sip_rtp_addr := "1.2.3.4", + sip_rtp_port := 1234, + cn_rtp_addr := "5.6.7.8", + cn_rtp_port := 5678 +} + +function f_init() runs on test_CT { + f_init_sip(vc_SIP, "Asterisk_Test"); + log("end of f_init"); +} + +type function void_fn(charstring id) runs on ConnHdlr; + +function f_start_handler(void_fn fn, ConnHdlrPars pars) +runs on test_CT return ConnHdlr { + var ConnHdlr vc_conn; + var charstring id := testcasename(); + + vc_conn := ConnHdlr.create(id); + + connect(vc_conn:SIP, vc_SIP:CLIENT); + connect(vc_conn:SIP_PROC, vc_SIP:CLIENT_PROC); + + vc_conn.start(f_handler_init(fn, id, pars)); + return vc_conn; +} + +private altstep as_Tguard() runs on ConnHdlr { + [] g_Tguard.timeout { + setverdict(fail, "Tguard timeout"); + mtc.stop; + } +} + +private function f_handler_init(void_fn fn, charstring id, ConnHdlrPars pars) +runs on ConnHdlr { + g_pars := pars; + g_Tguard.start(pars.t_guard); + activate(as_Tguard()); + + // Make sure the UA is deregistered before starting the test: + // sends REGISTER with Contact = "*" and Expires = 0 + //f_SIP_deregister(); + + /* call the user-supied test case function */ + fn.apply(id); +} + +altstep as_SIP_expect_req(template PDU_SIP_Request sip_expect) runs on ConnHdlr +{ + [] SIP.receive(sip_expect) -> value g_rx_sip_req; + [] SIP.receive { + log("FAIL: expected SIP message ", sip_expect); + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Received unexpected SIP message"); + } +} + +altstep as_SIP_expect_resp(template PDU_SIP_Response sip_expect) runs on ConnHdlr +{ + [] SIP.receive(sip_expect) -> value g_rx_sip_resp; + [] SIP.receive { + log("FAIL: expected SIP message ", sip_expect); + Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Received unexpected SIP message"); + } +} + +private function f_tr_Via_response(Via via_req) return template (present) Via { + template (present) SemicolonParam_List via_resp_params := ?; + + /*via_resp_params := { + { id := "rport", paramValue := int2str(mp_remote_sip_port) }, + { id := "received", paramValue := mp_remote_sip_host } + }; */ + return tr_Via_from(via_req.viaBody[0].sentBy, + via_resp_params); +} + +function f_SIP_register() runs on ConnHdlr return PDU_SIP_Response +{ + var template (present) PDU_SIP_Response exp; + + SIP.send(ts_SIP_REGISTER(g_pars.registrar_sip_url, + g_pars.registrar_sip_call_id, + g_pars.registrar_sip_record, + g_pars.registrar_sip_record, + g_pars.registrar_via, + g_pars.registrar_sip_seq_nr, + g_pars.local_contact, + ts_Expires("7200"))); + + exp := tr_SIP_Response_REGISTER_Unauthorized( + g_pars.registrar_sip_call_id, + g_pars.registrar_sip_record, + g_pars.registrar_sip_record, + f_tr_Via_response(g_pars.registrar_via), + *, + g_pars.registrar_sip_seq_nr); + as_SIP_expect_resp(exp); + + /* Do the registering after calculating the md5 hash, etc. */ + return g_rx_sip_resp; +} + +/* Successful MO Call, which is subsequently released by SIP side */ +private function f_TC_internal_registration(charstring id) runs on ConnHdlr { + + f_SIP_register(); + /* now call is fully established */ + f_sleep(2.0); + // f_SIP_deregister(); + setverdict(pass); +} + +testcase TC_internal_registration() runs on test_CT { + var ConnHdlrPars pars; + var ConnHdlr vc_conn; + f_init(); + pars := f_init_ConnHdlrPars(); + vc_conn := f_start_handler(refers(f_TC_internal_registration), pars); + vc_conn.done; +} + +control { + execute( TC_internal_registration() ); +} + +} diff --git a/asterisk/README.md b/asterisk/README.md new file mode 100644 index 0000000..c034bbb --- /dev/null +++ b/asterisk/README.md @@ -0,0 +1,16 @@ +* Asterisk_Tests.ttcn + +* external interfaces + * SIP (emulates SIP UAs) + * VoLTE (emulates IMS server) + +{% dot sip_tests.svg +digraph G { + rankdir=LR; + Asterisk [label="IUT\nAsterisk",shape="box"]; + ATS [label="ATS\nAsterisk_Tests.ttcn"]; + + ATS -> Asterisk [label="SIP"]; + ATS -> Asterisk [label="VoLTE (IMS)"]; +} +%} diff --git a/asterisk/expected-results.xml b/asterisk/expected-results.xml new file mode 100644 index 0000000..c1d9e2e --- /dev/null +++ b/asterisk/expected-results.xml @@ -0,0 +1,4 @@ +<?xml version="1.0"?> +<testsuite name='Titan' tests='9' failures='0' errors='0' skipped='0' inconc='0' time='MASKED'> + <testcase classname='Asterisk_Tests' name='TC_internal_registration' time='MASKED'/> +</testsuite> diff --git a/asterisk/gen_links.sh b/asterisk/gen_links.sh new file mode 100755 index 0000000..1fd6ecc --- /dev/null +++ b/asterisk/gen_links.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +BASEDIR=../deps + +. ../gen_links.sh.inc + +DIR=$BASEDIR/titan.Libraries.TCCUsefulFunctions/src +FILES="TCCInterface_Functions.ttcn TCCConversion_Functions.ttcn TCCConversion.cc TCCInterface.cc TCCInterface_ip.h" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.TestPorts.Common_Components.Socket-API/src +FILES="Socket_API_Definitions.ttcn" +gen_links $DIR $FILES + +# Required by MGCP and IPA +DIR=$BASEDIR/titan.TestPorts.IPL4asp/src +FILES="IPL4asp_Functions.ttcn IPL4asp_PT.cc IPL4asp_PT.hh IPL4asp_PortType.ttcn IPL4asp_Types.ttcn IPL4asp_discovery.cc IPL4asp_protocol_L234.hh" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.ProtocolModules.SDP/src +FILES="SDP_EncDec.cc SDP_Types.ttcn SDP_parse_.tab.c SDP_parse_.tab.h SDP_parse_parser.h SDP_parser.l +SDP_parser.y lex.SDP_parse_.c" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.ProtocolModules.RTP/src +FILES="RTP_EncDec.cc RTP_Types.ttcn" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.TestPorts.SIPmsg/src +FILES="SIP_parse.h SIP_parse.y SIP_parse_.tab.h SIPmsg_PT.hh SIPmsg_Types.ttcn SIP_parse.l SIP_parse_.tab.c SIPmsg_PT.cc SIPmsg_PortType.ttcn lex.SIP_parse_.c" +gen_links $DIR $FILES + +DIR=../library +FILES="Misc_Helpers.ttcn General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn Native_Functions.ttcn Native_FunctionDefs.cc " +FILES+="RTP_CodecPort.ttcn RTP_CodecPort_CtrlFunctDef.cc " +FILES+="SDP_Templates.ttcn " +FILES+="SIP_Emulation.ttcn SIP_Templates.ttcn " +gen_links $DIR $FILES + +ignore_pp_results diff --git a/asterisk/regen_makefile.sh b/asterisk/regen_makefile.sh new file mode 100755 index 0000000..3995b3d --- /dev/null +++ b/asterisk/regen_makefile.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +NAME=Asterisk_Tests + +FILES=" + *.c + *.ttcn + IPL4asp_PT.cc + IPL4asp_discovery.cc + Native_FunctionDefs.cc + RTP_CodecPort_CtrlFunctDef.cc + RTP_EncDec.cc + SDP_EncDec.cc + SIPmsg_PT.cc + TCCConversion.cc + TCCInterface.cc +" + +../regen-makefile.sh -e $NAME $FILES diff --git a/library/SIP_Emulation.ttcn b/library/SIP_Emulation.ttcn index e71c611..41e6975 100644 --- a/library/SIP_Emulation.ttcn +++ b/library/SIP_Emulation.ttcn @@ -72,6 +72,13 @@ sipVersion := ? } +private template PDU_SIP_Request tr_SIP_REGISTER := { + requestLine := tr_ReqLine(REGISTER_E), + msgHeader := t_SIP_msgHeader_any, + messageBody := *, + payload := * +} + private template PDU_SIP_Request tr_SIP_INVITE := { requestLine := tr_ReqLine(INVITE_E), msgHeader := t_SIP_msgHeader_any, @@ -79,7 +86,6 @@ payload := * } - template SipUrl tr_SIP_Url(template charstring user_or_num, template charstring host := *, template integer portField := *) := { @@ -286,6 +292,19 @@ } } + /* a ConnHdlr is sending us a SIP REGISTER: Forward to SIP port */ + [] CLIENT.receive(tr_SIP_REGISTER) -> value sip_req sender vc_conn { + var CallidString call_id := sip_req.msgHeader.callId.callid; + if (f_call_id_known(call_id)) { + /* re-register */ + vc_conn := f_comp_by_call_id(call_id); + } else { + /* new REGISTER: add to table */ + f_call_table_add(vc_conn, call_id); + } + SIP.send(sip_req); + } + /* a ConnHdlr is sending us a SIP INVITE: Forward to SIP port */ [] CLIENT.receive(tr_SIP_INVITE) -> value sip_req sender vc_conn { var CallidString call_id := sip_req.msgHeader.callId.callid; diff --git a/library/SIP_Templates.ttcn b/library/SIP_Templates.ttcn index f48d137..7b5e94f 100644 --- a/library/SIP_Templates.ttcn +++ b/library/SIP_Templates.ttcn @@ -525,6 +525,26 @@ payload := omit } +/* Expect during first REGISTER when authorization is requried: */ +template (present) PDU_SIP_Response +tr_SIP_Response_REGISTER_Unauthorized( + template CallidString call_id, + template SipAddr from_addr, + template SipAddr to_addr, + template (present) Via via := tr_Via_from(?), + template Contact contact := *, + template integer seq_nr := ?, + template charstring method := "REGISTER", + template integer status_code := 401, + template charstring reason := "Unauthorized", + template charstring body := *) := { + statusLine := tr_SIP_StatusLine(status_code, reason), + msgHeader := tr_SIP_msgh_std(call_id, from_addr, to_addr, contact, + via, + method, *, seq_nr), + messageBody := body, + payload := omit +} /* RFC 3261 8.1.1.5: * "The sequence number value MUST be expressible as a 32-bit unsigned integer -- To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/36490?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-ttcn3-hacks Gerrit-Branch: master Gerrit-Change-Id: I66f776d5df6fb5dc488d9e589b84a6b2385406e8 Gerrit-Change-Number: 36490 Gerrit-PatchSet: 1 Gerrit-Owner: pespin <pes...@sysmocom.de> Gerrit-MessageType: newchange