WIP: asterisk: Initial IMS registration

Change-Id: I30261472587e2386a5cd8ffce2aee6e5e3ada6ff
This commit is contained in:
Pau Espin 2024-04-25 16:24:59 +02:00
parent 915580ef91
commit c6b09407cd
3 changed files with 311 additions and 4 deletions

View File

@ -25,10 +25,14 @@ modulepar {
}
const charstring AMI_FIELD_ACTION := "Action";
const charstring AMI_FIELD_ACTION_ID := "ActionId";
const charstring AMI_FIELD_USERNAME := "Username";
const charstring AMI_FIELD_SECRET := "Secret";
const charstring AMI_FIELD_RESPONSE := "Response";
/* Extensions: */
const charstring AMI_FIELD_REGISTRATION := "Registration";
type record AMI_Field {
charstring key,
charstring val
@ -69,28 +73,46 @@ tr_AMI_Field(template (present) charstring key := ?,
template (value) AMI_Field
ts_AMI_Field_Action(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION, val);
template (value) AMI_Field
ts_AMI_Field_ActionId(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_ACTION_ID, val);
template (value) AMI_Field
ts_AMI_Field_Username(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_USERNAME, val);
template (value) AMI_Field
ts_AMI_Field_Secret(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_SECRET, val);
/* Extensions: */
template (value) AMI_Field
ts_AMI_Field_Registration(template (value) charstring val) := ts_AMI_Field(AMI_FIELD_REGISTRATION, val);
template (present) AMI_Field
tr_AMI_Field_Action(template (present) charstring val := ?) := tr_AMI_Field(AMI_FIELD_ACTION, val);
template (present) AMI_Field
tr_AMI_Field_ActionId(template (present) charstring val := ?) := tr_AMI_Field(AMI_FIELD_ACTION_ID, val);
template (present) AMI_Field
tr_AMI_Field_Username(template (present) charstring val := ?) := tr_AMI_Field(AMI_FIELD_USERNAME, val);
template (present) AMI_Field
tr_AMI_Field_Secret(template (present) charstring val := ?) := tr_AMI_Field(AMI_FIELD_SECRET, val);
template (present) AMI_Field
tr_AMI_Field_Response(template (present) charstring val := ?) := tr_AMI_Field(AMI_FIELD_RESPONSE, val);
/* Extensions: */
template (present) AMI_Field
tr_AMI_Field_Registration(template (present) charstring val := ?) := tr_AMI_Field(AMI_FIELD_REGISTRATION, val);
template (present) AMI_Field
tr_AMI_Field_ResponseSuccess := tr_AMI_Field(AMI_FIELD_RESPONSE, "Success");
/*
/***********************
* Message Templates:
***********************/
/*
* ACTIONS
*/
/* Action: Login
* Username: <value>
* Secret: <value>
*/
template (value) AMI_Msg
ts_AMI_Action_Login(charstring username, charstring secret) := {
ts_AMI_Field_Action("Login"),
@ -106,15 +128,101 @@ tr_AMI_Action_Login(template(present) charstring username := ?,
tr_AMI_Field_Secret(secret)
);
/* Action: PJSIPRegister
* ActionID: <value>
* Registration: volte_ims
*/
template (value) AMI_Msg
ts_AMI_Action_PJSIPRegister(template (value) charstring registration := "volte_ims",
template (value) charstring action_id := "0001") := {
ts_AMI_Field_Action("PJSIPRegister"),
ts_AMI_Field_ActionId(action_id),
ts_AMI_Field_Registration(registration)
};
template (present) AMI_Msg
tr_AMI_Action_PJSIPRegister(template (present) charstring registration := ?,
template (present) charstring action_id := ?) := {
tr_AMI_Field_Action("PJSIPRegister"),
tr_AMI_Field_ActionId(action_id),
tr_AMI_Field_Registration(registration)
};
/*
* RESPONSES
*/
/* Response: Success
*/
template (present) AMI_Msg
tr_AMI_Response_Success := superset(
tr_AMI_Field_ResponseSuccess
);
/* Response: Success
* ActionId: <value>
*/
template (present) AMI_Msg
tr_AMI_Response_Success_ActionId(template (present) charstring action_id := ?) := superset(
tr_AMI_Field_ResponseSuccess,
tr_AMI_Field_ActionId(action_id)
);
/*
* Functions:
*/
/* Generate a random "ActionId" value: */
function f_gen_action_id() return charstring {
return hex2str(f_rnd_hexstring(16));
}
function f_ami_msg_find(AMI_Msg msg,
template (present) charstring key := ?)
return template (omit) AMI_Field {
var integer i;
for (i := 0; i < lengthof(msg); i := i + 1) {
if (not ispresent(msg[i])) {
continue;
}
if (match(msg[i].key, key)) {
return msg[i];
}
}
return omit;
}
function f_ami_msg_find_or_fail(AMI_Msg msg,
template (present) charstring key := ?)
return AMI_Field {
var template (omit) AMI_Field field;
field := f_ami_msg_find(msg, key);
if (istemplatekind(field, "omit")) {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str("Key ", key, " not found in ", msg));
}
return valueof(field);
}
function f_ami_msg_get_value(AMI_Msg msg,
template (present) charstring key := ?)
return template (omit) charstring {
var template (omit) AMI_Field field;
field := f_ami_msg_find(msg, key);
if (istemplatekind(field, "omit")) {
return omit;
}
return field.val;
}
function f_ami_msg_get_value_or_fail(AMI_Msg msg,
template (present) charstring key := ?)
return template charstring {
var AMI_Field field;
field := f_ami_msg_find_or_fail(msg, key);
return field.val;
}
private function f_ami_wait_for_prompt_str(TELNETasp_PT pt, charstring log_label := "(?)")
return charstring {
var charstring rx, buf := "";
@ -172,11 +280,23 @@ function f_ami_transceive_match(TELNETasp_PT pt,
function f_ami_transceive_match_response_success(TELNETasp_PT pt,
template (value) AMI_Msg tx_msg) {
f_ami_transceive_match(pt, tx_msg, tr_AMI_Response_Success);
var template (present) AMI_Msg exp_resp;
var template (omit) charstring action_id := f_ami_msg_get_value(valueof(tx_msg), AMI_FIELD_ACTION_ID);
if (isvalue(action_id)) {
exp_resp := tr_AMI_Response_Success_ActionId(action_id);
} else {
exp_resp := tr_AMI_Response_Success;
}
f_ami_transceive_match(pt, tx_msg, exp_resp);
}
function f_ami_action_login(TELNETasp_PT pt, charstring username, charstring secret) {
f_ami_transceive_match_response_success(pt, ts_AMI_Action_Login(username, secret));
}
function f_ami_action_PJSIPRegister(TELNETasp_PT pt, charstring register) {
var charstring reg_action_id := f_gen_action_id();
f_ami_transceive_match_response_success(pt, ts_AMI_Action_PJSIPRegister(register, reg_action_id));
}
}

View File

@ -37,6 +37,7 @@ modulepar {
charstring mp_local_ims_host := "127.0.0.3";
integer mp_local_ims_port := 5060;
charstring mp_ims_imsi := "001010000000002";
/* Asterisk AMI: */
charstring mp_ami_user := "test_user";
@ -68,6 +69,15 @@ function f_init_ConnHdlrPars(integer idx := 1) runs on test_CT return SIPConnHdl
return valueof(pars);
}
function f_init_IMS_ConnHdlrPars(integer idx := 1) runs on test_CT return IMS_ConnHdlrPars {
var template (value) IMS_CallPars cp := t_IMS_CallPars(mp_local_sip_host, 1234 + 2*idx);
var template (value) IMS_ConnHdlrPars pars := t_IMS_Pars(mp_local_ims_host,
mp_local_ims_port,
mp_ims_imsi,
cp := cp);
return valueof(pars);
}
/* Initialize connection towards Asterisk AMI */
private function f_init_ami() runs on test_CT {
map(self:AMI, system:AMI);
@ -111,6 +121,22 @@ runs on test_CT return SIPConnHdlr {
return vc_conn;
}
function f_start_handler_IMS(ims_void_fn fn, IMS_ConnHdlrPars pars)
runs on test_CT return IMS_ConnHdlr {
var IMS_ConnHdlr vc_conn;
var charstring id := testcasename() & "-IMS_ConnHdlr-" & pars.user;
vc_conn := IMS_ConnHdlr.create(id) alive;
connect(vc_conn:SIP, vc_SIP:CLIENT);
connect(vc_conn:SIP_PROC, vc_SIP:CLIENT_PROC);
connect(vc_conn:COORD, self:IMS_COORD);
vc_conn.start(f_ims_handler_init(fn, id, pars));
return vc_conn;
}
/* Test SIP registration of local clients */
private function f_TC_internal_registration(charstring id) runs on SIPConnHdlr {
@ -290,12 +316,46 @@ testcase TC_selftest() runs on test_CT {
setverdict(pass);
}
/* Test SIP registration of local clients */
private function f_TC_ims_registration(charstring id) runs on IMS_ConnHdlr {
as_IMS_register();
setverdict(pass);
}
testcase TC_ims_registration() runs on test_CT {
var IMS_ConnHdlrPars pars;
var IMS_ConnHdlr vc_conn;
f_init();
pars := f_init_IMS_ConnHdlrPars();
vc_conn := f_start_handler_IMS(refers(f_TC_ims_registration), pars);
/* Trigger registration: */
f_ami_action_PJSIPRegister(AMI, "volte_ims");
/* TODO: Rx "Event: AuthRequest" */
/* TODO: Tx "Action: AuthResponse" */
/* TODO: Rx "Response: Success" */
/* TODO: once registration is successful, rx:
* Event: Registry
* ChannelType: pjsip
* Username: <value>
* Domain: <value>
* Status: <value>
* Cause: <value> */
/* TODO: test "Action: PJSIPUnregister" */
/* TODO: in another test emulating a call, test "Action: DedicatedBearerStatus" */
vc_conn.done;
}
control {
execute( TC_internal_registration() );
execute( TC_internal_call_momt() );
execute( TC_internal_call_all_2registered() );
execute( TC_internal_call_all_3registered() );
execute( TC_internal_call_all_4registered() );
execute( TC_ims_registration() );
}
}

View File

@ -43,8 +43,8 @@ type record of IMS_ConnHdlr IMS_ConnHdlrList;
type record IMS_ConnHdlrPars {
float t_guard,
charstring remote_sip_host,
uint16_t remote_sip_port,
charstring remote_sip_host optional,
uint16_t remote_sip_port optional,
charstring user,
charstring display_name,
charstring password,
@ -66,6 +66,10 @@ type record IMS_CallParsMT {
/* Whether to expect CANCEL instead of ACK as answer to our OK */
boolean exp_cancel
}
template (value) IMS_CallParsMT t_IMS_CallParsMT := {
wait_coord_cmd_pickup := false,
exp_cancel := false
}
type record IMS_CallPars {
SipAddr calling optional,
@ -85,4 +89,127 @@ type record IMS_CallPars {
IMS_CallParsMT mt
}
template (value) IMS_CallPars t_IMS_CallPars(charstring local_rtp_addr,
uint16_t local_rtp_port := 0,
template (omit) SipAddr calling := omit,
template (omit) SipAddr called := omit) := {
calling := calling,
called := called,
from_addr := omit,
to_addr := omit,
sip_call_id := hex2str(f_rnd_hexstring(15)),
sip_seq_nr := f_sip_rand_seq_nr(),
sip_body := omit,
local_rtp_addr := local_rtp_addr,
local_rtp_port := local_rtp_port,
peer_sdp := omit,
mt := t_IMS_CallParsMT
}
template (value) IMS_ConnHdlrPars t_IMS_Pars(charstring local_sip_host,
uint16_t local_sip_port,
charstring user,
charstring display_name := "Anonymous",
charstring password := "secret",
template (omit) IMS_CallPars cp := omit) := {
t_guard := 30.0,
remote_sip_host := omit,
remote_sip_port := omit,
user := user,
display_name := f_sip_str_quote(display_name),
password := password,
registrar_sip_req_uri := valueof(ts_SipUrlHost(local_sip_host)),
registrar_sip_record := ts_SipAddr(ts_HostPort(local_sip_host),
ts_UserInfo(user),
f_sip_str_quote(display_name)),
registrar_sip_call_id := hex2str(f_rnd_hexstring(15)) & "@" & local_sip_host,
registrar_sip_seq_nr := f_sip_rand_seq_nr(),
local_via := ts_Via_from(ts_HostPort(local_sip_host, local_sip_port)),
local_sip_url_ext := ts_SipUrl(ts_HostPort(local_sip_host, local_sip_port),
ts_UserInfo(user)),
local_sip_record := ts_SipAddr(ts_HostPort(local_sip_host),
ts_UserInfo(user)),
local_contact := valueof(ts_Contact({
ts_ContactAddress(
ts_Addr_Union_SipUrl(ts_SipUrl(ts_HostPort(
local_sip_host,
local_sip_port),
ts_UserInfo(user))),
omit)
})),
cp := cp
}
private altstep as_Tguard() runs on IMS_ConnHdlr {
[] g_Tguard.timeout {
setverdict(fail, "Tguard timeout");
mtc.stop;
}
}
type function ims_void_fn(charstring id) runs on IMS_ConnHdlr;
function f_ims_handler_init(ims_void_fn fn, charstring id, IMS_ConnHdlrPars pars)
runs on IMS_ConnHdlr {
g_name := id;
g_pars := pars;
g_Tguard.start(pars.t_guard);
activate(as_Tguard());
/* call the user-supied test case function */
fn.apply(id);
}
private altstep as_SIP_fail_req(charstring exp_msg_str := "") runs on IMS_ConnHdlr
{
var PDU_SIP_Request sip_req;
[] SIP.receive(PDU_SIP_Request:?) -> value sip_req {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str(g_name & ": Received unexpected SIP Req message := ", sip_req, "\nvs exp := ", exp_msg_str));
}
}
private altstep as_SIP_fail_resp(charstring exp_msg_str := "") runs on IMS_ConnHdlr
{
var PDU_SIP_Response sip_resp;
[] SIP.receive(PDU_SIP_Response:?) -> value sip_resp {
Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
log2str(g_name & ": Received unexpected SIP Resp message := ", sip_resp, "\nvs exp := ", exp_msg_str));
}
}
/* Peer is calling us, accept it: */
altstep as_IMS_register(boolean exp_update_to_direct_rtp := true,
boolean fail_others := true) runs on IMS_ConnHdlr
{
var template (present) PDU_SIP_Request exp_req :=
tr_SIP_REGISTER(g_pars.registrar_sip_req_uri,
?,
tr_SipAddr(),
tr_SipAddr(),
tr_Via_from(?));
var charstring sip_expect_str := log2str(exp_req);
[] SIP.receive(exp_req) -> value g_rx_sip_req {
var template (value) PDU_SIP_Response tx_resp;
var Via via;
var charstring tx_sdp;
via := g_rx_sip_req.msgHeader.via;
/* Tx 200 OK
* TODO: Tx Unauthorized instead, with IMS params */
tx_resp := ts_SIP_Response(g_pars.cp.sip_call_id,
g_pars.cp.from_addr,
g_pars.cp.to_addr,
"REGISTER", 200,
g_pars.cp.sip_seq_nr,
"OK",
via);
SIP.send(tx_resp);
}
[fail_others] as_SIP_fail_resp(sip_expect_str);
[fail_others] as_SIP_fail_req(sip_expect_str);
}
}