msc_vlr_test_call: test codecs resolution

Related: SYS#5066
Change-Id: I879ec61f523ad4ffc69a0b02810591f7c0261ff9
This commit is contained in:
Neels Hofmeyr 2022-01-14 03:35:08 +01:00
parent 7f4fd0f2a7
commit 4b53b035b8
4 changed files with 3739 additions and 15 deletions

View File

@ -24,6 +24,7 @@
#include "msc_vlr_tests.h" #include "msc_vlr_tests.h"
#include <osmocom/msc/gsm_04_08.h> #include <osmocom/msc/gsm_04_08.h>
#include <osmocom/msc/codec_sdp_cc_t9n.h>
#define mncc_sends_to_cc(MSG_TYPE, MNCC) do { \ #define mncc_sends_to_cc(MSG_TYPE, MNCC) do { \
(MNCC)->msg_type = MSG_TYPE; \ (MNCC)->msg_type = MSG_TYPE; \
@ -163,6 +164,44 @@ static void lu_utran_tmsi()
vlr_subscr_put(vsub, __func__); vlr_subscr_put(vsub, __func__);
} }
static void lu_geran_noauth()
{
rx_from_ran = OSMO_RAT_GERAN_A;
net->authentication_required = false;
net->vlr->cfg.assign_tmsi = false;
btw("Location Update request causes a GSUP LU request to HLR");
lu_result_sent = RES_NONE;
gsup_expect_tx("04010809710000000156f0280102" VLR_TO_HLR);
ms_sends_msg("0508" /* MM LU */
"7" /* ciph key seq: no key available */
"0" /* LU type: normal */
"09f107" "0017" /* LAI, LAC */
"57" /* classmark 1: R99, early classmark, no power lvl */
"089910070000106005" /* IMSI */
"3303575886" /* classmark 2 */
);
OSMO_ASSERT(gsup_tx_confirmed);
VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d");
btw("HLR sends _INSERT_DATA_REQUEST, VLR responds with _INSERT_DATA_RESULT");
gsup_rx("10010809710000000156f00804036470f1" HLR_TO_VLR,
"12010809710000000156f0" VLR_TO_HLR);
VERBOSE_ASSERT(lu_result_sent, == RES_NONE, "%d");
btw("HLR also sends GSUP _UPDATE_LOCATION_RESULT");
expect_bssap_clear();
gsup_rx("06010809710000000156f0" HLR_TO_VLR, NULL);
btw("LU was successful, and the conn has already been closed");
VERBOSE_ASSERT(lu_result_sent, == RES_ACCEPT, "%d");
VERBOSE_ASSERT(bssap_clear_sent, == true, "%d");
ran_sends_clear_complete();
EXPECT_CONN_COUNT(0);
}
static void test_call_mo() static void test_call_mo()
{ {
struct gsm_mncc mncc = { struct gsm_mncc mncc = {
@ -232,7 +271,7 @@ static void test_call_mo()
btw("Assignment succeeds, triggering MNCC_RTP_CREATE ack to MNCC"); btw("Assignment succeeds, triggering MNCC_RTP_CREATE ack to MNCC");
cc_to_mncc_expect_tx("", MNCC_RTP_CREATE); cc_to_mncc_expect_tx("", MNCC_RTP_CREATE);
ms_sends_assignment_complete(CODEC_AMR_8000_1); ms_sends_assignment_complete("AMR");
OSMO_ASSERT(cc_to_mncc_tx_confirmed); OSMO_ASSERT(cc_to_mncc_tx_confirmed);
btw("MNCC says that's fine"); btw("MNCC says that's fine");
@ -359,7 +398,7 @@ static void test_call_mt()
btw("Assignment completes, triggering CRCX to CN"); btw("Assignment completes, triggering CRCX to CN");
expect_crcx(RTP_TO_CN); expect_crcx(RTP_TO_CN);
ms_sends_assignment_complete(CODEC_AMR_8000_1); ms_sends_assignment_complete("AMR");
btw("MNCC sends MNCC_RTP_CREATE, which first waits for the CN side RTP"); btw("MNCC sends MNCC_RTP_CREATE, which first waits for the CN side RTP");
mncc_sends_to_cc(MNCC_RTP_CREATE, &mncc); mncc_sends_to_cc(MNCC_RTP_CREATE, &mncc);
@ -481,7 +520,7 @@ static void test_call_mt2()
OSMO_ASSERT(iu_rab_assignment_sent); OSMO_ASSERT(iu_rab_assignment_sent);
btw("Assignment completes, triggering CRCX to CN"); btw("Assignment completes, triggering CRCX to CN");
ms_sends_assignment_complete(CODEC_AMR_8000_1); ms_sends_assignment_complete("AMR");
btw("When the CN side RTP address is known, ack MNCC_RTP_CREATE with full SDP"); btw("When the CN side RTP address is known, ack MNCC_RTP_CREATE with full SDP");
cc_to_mncc_expect_tx("", MNCC_RTP_CREATE); cc_to_mncc_expect_tx("", MNCC_RTP_CREATE);
@ -584,7 +623,7 @@ static void test_call_mo_to_unknown()
btw("Assignment succeeds, triggering MNCC_RTP_CREATE ack to MNCC"); btw("Assignment succeeds, triggering MNCC_RTP_CREATE ack to MNCC");
cc_to_mncc_expect_tx("", MNCC_RTP_CREATE); cc_to_mncc_expect_tx("", MNCC_RTP_CREATE);
ms_sends_assignment_complete(CODEC_AMR_8000_1); ms_sends_assignment_complete("AMR");
OSMO_ASSERT(cc_to_mncc_tx_confirmed); OSMO_ASSERT(cc_to_mncc_tx_confirmed);
btw("MNCC says that's fine"); btw("MNCC says that's fine");
@ -680,7 +719,7 @@ static void test_call_mo_to_unknown_timeout()
btw("Assignment succeeds, triggering MNCC_RTP_CREATE ack to MNCC"); btw("Assignment succeeds, triggering MNCC_RTP_CREATE ack to MNCC");
cc_to_mncc_expect_tx("", MNCC_RTP_CREATE); cc_to_mncc_expect_tx("", MNCC_RTP_CREATE);
ms_sends_assignment_complete(CODEC_AMR_8000_1); ms_sends_assignment_complete("AMR");
OSMO_ASSERT(cc_to_mncc_tx_confirmed); OSMO_ASSERT(cc_to_mncc_tx_confirmed);
btw("MNCC says that's fine"); btw("MNCC says that's fine");
@ -709,6 +748,694 @@ static void test_call_mo_to_unknown_timeout()
comment_end(); comment_end();
} }
#define LIST_END 0xffff
struct codec_test {
const char *desc;
/* What to send during Complete Layer 3 as Codec List (BSS Supported). List ends with a LIST_END entry */
enum gsm0808_speech_codec_type mo_rx_compl_l3_codec_list_bss_supported[8];
/* What to send during CC Setup as MS Bearer Capability. List ends with a LIST_END entry */
enum gsm48_bcap_speech_ver mo_rx_ms_bcap[8];
/* What codecs should osmo-msc send in the MNCC_SETUP_IND message.
* Just the SDP subtype names like "GSM", "GSM-EFR", "AMR", ..., list ends with NULL entry */
const char *mo_tx_sdp_mncc_setup_ind[8];
/* What codecs the remote call leg should send as SDP via MNCC during MNCC_RTP_CREATE (if any). */
const char *mo_rx_sdp_mncc_rtp_create[8];
/* What the MSC should send as Channel Type IE in the Assignment Command to the BSS. List ends with a
* LIST_END entry */
enum gsm0808_permitted_speech mo_tx_assignment_perm_speech[8];
/* What codec to assign in the Assignment Complete's Codec (Chosen) IE. Just a subtype name. */
const char *mo_rx_assigned_codec;
/* MO acks the MNCC_RTP_CREATE with these codecs (if any). */
const char *mo_tx_sdp_mncc_rtp_create[8];
/* mt_rx_sdp_mncc_setup_req == mo_tx_sdp_mncc_rtp_create */
#define mt_rx_sdp_mncc_setup_req mo_tx_sdp_mncc_rtp_create
enum gsm0808_speech_codec_type mt_rx_compl_l3_codec_list_bss_supported[8];
bool expect_codec_mismatch_on_paging_response;
enum gsm48_bcap_speech_ver mt_tx_cc_setup_bcap[8];
enum gsm48_bcap_speech_ver mt_rx_ms_bcap[8];
bool expect_codec_mismatch_on_cc_call_conf;
const char *mt_tx_sdp_mncc_call_conf_ind[8];
enum gsm0808_permitted_speech mt_tx_assignment_perm_speech[8];
const char *mt_rx_assigned_codec;
const char *mt_rx_sdp_mncc_rtp_create[8];
const char *mt_tx_sdp_mncc_rtp_create[8];
const char *mt_tx_sdp_mncc_alert_ind[8];
const char *mt_tx_sdp_mncc_setup_cnf[8];
const char *mt_rx_sdp_mncc_setup_compl_req[8];
/* mo_rx_sdp_mncc_alert_req == mt_tx_sdp_mncc_alert_ind */
#define mo_rx_sdp_mncc_alert_req mt_tx_sdp_mncc_alert_ind
#define mo_rx_sdp_mncc_setup_rsp mt_tx_sdp_mncc_alert_ind
const char *mo_tx_sdp_mncc_setup_compl_ind[8];
};
#define CODEC_LIST_ALL_GSM { \
GSM0808_SCT_FR1, \
GSM0808_SCT_FR2, \
GSM0808_SCT_FR3, \
GSM0808_SCT_HR1, \
GSM0808_SCT_HR3, \
LIST_END \
}
#define BCAP_ALL_GSM { \
GSM48_BCAP_SV_AMR_F, \
GSM48_BCAP_SV_AMR_H, \
GSM48_BCAP_SV_AMR_OH, \
GSM48_BCAP_SV_EFR, \
GSM48_BCAP_SV_FR, \
GSM48_BCAP_SV_HR, \
LIST_END \
}
#define PERM_SPEECH_ALL_GSM { \
GSM0808_PERM_FR3, \
GSM0808_PERM_HR3, \
GSM0808_PERM_FR2, \
GSM0808_PERM_FR1, \
GSM0808_PERM_HR1, \
LIST_END \
}
#define SDP_CODECS_ALL_GSM { \
"AMR", \
"GSM-EFR", \
"GSM", \
"GSM-HR-08", \
}
static const struct codec_test codec_tests[] = {
{
.desc = "AMR picked by both MO and MT",
.mo_rx_compl_l3_codec_list_bss_supported = CODEC_LIST_ALL_GSM,
.mo_rx_ms_bcap = BCAP_ALL_GSM,
.mo_tx_sdp_mncc_setup_ind = SDP_CODECS_ALL_GSM,
.mo_rx_sdp_mncc_rtp_create = {},
.mo_tx_assignment_perm_speech = PERM_SPEECH_ALL_GSM,
.mo_rx_assigned_codec = "AMR",
.mo_tx_sdp_mncc_rtp_create = { "AMR" },
/* mt_rx_sdp_mncc_setup_req == mo_tx_sdp_mncc_rtp_create */
.mt_rx_compl_l3_codec_list_bss_supported = CODEC_LIST_ALL_GSM,
.mt_tx_cc_setup_bcap = {
GSM48_BCAP_SV_AMR_F,
GSM48_BCAP_SV_AMR_H,
GSM48_BCAP_SV_AMR_OH,
LIST_END
},
.mt_rx_ms_bcap = BCAP_ALL_GSM,
.mt_tx_sdp_mncc_call_conf_ind = {},
.mt_rx_sdp_mncc_rtp_create = {},
.mt_tx_assignment_perm_speech = { GSM0808_PERM_FR3, GSM0808_PERM_HR3, LIST_END },
.mt_rx_assigned_codec = "AMR",
.mt_tx_sdp_mncc_rtp_create = { "AMR" },
.mt_tx_sdp_mncc_alert_ind = { "AMR" },
.mt_tx_sdp_mncc_setup_cnf = { "AMR" },
.mo_tx_sdp_mncc_setup_compl_ind = {},
},
{
.desc = "FR1 picked by MO from Codec List (BSS Supported), MT hence also picks FR1",
.mo_rx_compl_l3_codec_list_bss_supported = { GSM0808_SCT_FR1, LIST_END },
.mo_rx_ms_bcap = BCAP_ALL_GSM,
.mo_tx_sdp_mncc_setup_ind = { "GSM" },
.mo_rx_sdp_mncc_rtp_create = {},
.mo_tx_assignment_perm_speech = { GSM0808_PERM_FR1, LIST_END },
.mo_rx_assigned_codec = "GSM",
.mo_tx_sdp_mncc_rtp_create = { "GSM" },
/* .mt_rx_sdp_mncc_setup_req == .mo_tx_sdp_mncc_setup_ind */
.mt_rx_compl_l3_codec_list_bss_supported = CODEC_LIST_ALL_GSM,
.mt_tx_cc_setup_bcap = { GSM48_BCAP_SV_FR, LIST_END },
.mt_rx_ms_bcap = BCAP_ALL_GSM,
.mt_tx_sdp_mncc_call_conf_ind = {},
.mt_rx_sdp_mncc_rtp_create = {},
.mt_tx_assignment_perm_speech = { GSM0808_PERM_FR1, LIST_END },
.mt_rx_assigned_codec = "GSM",
.mt_tx_sdp_mncc_rtp_create = { "GSM" },
.mt_tx_sdp_mncc_alert_ind = { "GSM" },
.mt_tx_sdp_mncc_setup_cnf = { "GSM" },
.mo_tx_sdp_mncc_setup_compl_ind = {},
},
{
.desc = "FR1 picked by MO from Bearer Cap, MT hence also picks FR1",
.mo_rx_compl_l3_codec_list_bss_supported = CODEC_LIST_ALL_GSM,
.mo_rx_ms_bcap = { GSM48_BCAP_SV_FR, LIST_END },
.mo_tx_sdp_mncc_setup_ind = { "GSM" },
.mo_rx_sdp_mncc_rtp_create = {},
.mo_tx_assignment_perm_speech = { GSM0808_PERM_FR1, LIST_END },
.mo_rx_assigned_codec = "GSM",
.mo_tx_sdp_mncc_rtp_create = { "GSM" },
/* .mt_rx_sdp_mncc_setup_req == .mo_tx_sdp_mncc_setup_ind */
.mt_rx_compl_l3_codec_list_bss_supported = CODEC_LIST_ALL_GSM,
.mt_tx_cc_setup_bcap = { GSM48_BCAP_SV_FR, LIST_END },
.mt_rx_ms_bcap = BCAP_ALL_GSM,
.mt_tx_sdp_mncc_call_conf_ind = {},
.mt_rx_sdp_mncc_rtp_create = {},
.mt_tx_assignment_perm_speech = { GSM0808_PERM_FR1, LIST_END },
.mt_rx_assigned_codec = "GSM",
.mt_tx_sdp_mncc_rtp_create = { "GSM" },
.mt_tx_sdp_mncc_alert_ind = { "GSM" },
.mt_tx_sdp_mncc_setup_cnf = { "GSM" },
.mo_tx_sdp_mncc_setup_compl_ind = {},
},
{
.desc = "FR1 picked by MT's Codec List (BSS Supported), hence MO also picks FR1 (EXPECTED FAILURE)",
/* Currently the MO Assignment happens before MT gets a chance to send its available codecs.
* So even though the MO side would be able to assign FR1 and match MT, this is established too late
* and MO mismatches MT. This can only be fixed by a) moving MO Assignment to after MT Assignment
* or b) doing a Channel Mode Change or re-assignment after MT Assignment -- since re-assigning might
* need an lchan type change and means more overhead, a) would be the best option. */
.mo_rx_compl_l3_codec_list_bss_supported = CODEC_LIST_ALL_GSM,
.mo_rx_ms_bcap = BCAP_ALL_GSM,
.mo_tx_sdp_mncc_setup_ind = SDP_CODECS_ALL_GSM,
.mo_rx_sdp_mncc_rtp_create = {},
.mo_tx_assignment_perm_speech = PERM_SPEECH_ALL_GSM,
.mo_rx_assigned_codec = "AMR", /* <- Early Assignment means codec mismatch */
.mo_tx_sdp_mncc_rtp_create = { "AMR" },
.mt_rx_compl_l3_codec_list_bss_supported = { GSM0808_SCT_FR1, LIST_END },
.expect_codec_mismatch_on_paging_response = true,
/* The mismatching codec AMR vs. GSM means the call fails (in the lack of transcoding) */
},
{
.desc = "FR1 picked by MT's MS Bearer Capability, hence MO also picks FR1 (EXPECTED FAILURE)",
/* Like above, MO Assignment happens too early to be able to match MT's codec availability. */
.mo_rx_compl_l3_codec_list_bss_supported = CODEC_LIST_ALL_GSM,
.mo_rx_ms_bcap = BCAP_ALL_GSM,
.mo_tx_sdp_mncc_setup_ind = SDP_CODECS_ALL_GSM,
.mo_rx_sdp_mncc_rtp_create = {},
.mo_tx_assignment_perm_speech = PERM_SPEECH_ALL_GSM,
.mo_rx_assigned_codec = "AMR", /* <- Early Assignment means codec mismatch */
.mo_tx_sdp_mncc_rtp_create = { "AMR" },
.mt_rx_compl_l3_codec_list_bss_supported = CODEC_LIST_ALL_GSM,
.mt_tx_cc_setup_bcap = {
GSM48_BCAP_SV_AMR_F,
GSM48_BCAP_SV_AMR_H,
GSM48_BCAP_SV_AMR_OH,
LIST_END
},
.mt_rx_ms_bcap = { GSM48_BCAP_SV_FR, LIST_END },
.mt_tx_sdp_mncc_call_conf_ind = {},
.mt_rx_sdp_mncc_rtp_create = {},
.mt_tx_assignment_perm_speech = { GSM0808_PERM_FR3, GSM0808_PERM_HR3, LIST_END },
.expect_codec_mismatch_on_cc_call_conf = true,
/* The mismatching codec AMR vs. GSM means the call fails (in the lack of transcoding) */
},
};
static char namebuf[4][1024];
static int use_namebuf = 0;
static const char *codec_list_name(const enum gsm0808_speech_codec_type compl_l3_codec_list_bss_supported[])
{
struct osmo_strbuf sb = { .buf = namebuf[use_namebuf], .len = sizeof(namebuf[0]) };
use_namebuf = (use_namebuf + 1) % ARRAY_SIZE(namebuf);
const enum gsm0808_speech_codec_type *pos;
sb.buf[0] = '\0';
for (pos = compl_l3_codec_list_bss_supported; *pos != LIST_END; pos++)
OSMO_STRBUF_PRINTF(sb, " %s", gsm0808_speech_codec_type_name(*pos));
return sb.buf;
}
static const struct gsm0808_speech_codec_list *codec_list(const enum gsm0808_speech_codec_type compl_l3_codec_list_bss_supported[])
{
static struct gsm0808_speech_codec_list scl;
scl = (struct gsm0808_speech_codec_list){};
const enum gsm0808_speech_codec_type *pos;
for (pos = compl_l3_codec_list_bss_supported; *pos != LIST_END; pos++) {
scl.codec[scl.len] = (struct gsm0808_speech_codec){
.fi = true,
.type = *pos,
};
scl.len++;
}
return &scl;
}
static const char *bcap_name(const enum gsm48_bcap_speech_ver ms_bcap[])
{
struct osmo_strbuf sb = { .buf = namebuf[use_namebuf], .len = sizeof(namebuf[0]) };
use_namebuf = (use_namebuf + 1) % ARRAY_SIZE(namebuf);
const enum gsm48_bcap_speech_ver *pos;
sb.buf[0] = '\0';
for (pos = ms_bcap; *pos != LIST_END; pos++) {
const struct codec_mapping *m = codec_mapping_by_speech_ver(*pos);
OSMO_STRBUF_PRINTF(sb, " %s", m? m->sdp.subtype_name : "NULL");
}
return sb.buf;
}
static const char *perm_speech_name(const enum gsm0808_permitted_speech perm_speech[])
{
struct osmo_strbuf sb = { .buf = namebuf[use_namebuf], .len = sizeof(namebuf[0]) };
use_namebuf = (use_namebuf + 1) % ARRAY_SIZE(namebuf);
const enum gsm0808_permitted_speech *pos;
sb.buf[0] = '\0';
for (pos = perm_speech; *pos != LIST_END; pos++) {
OSMO_STRBUF_PRINTF(sb, " %s", gsm0808_permitted_speech_name(*pos));
}
return sb.buf;
}
static const char *strlist_name(const char *const* strs)
{
struct osmo_strbuf sb = { .buf = namebuf[use_namebuf], .len = sizeof(namebuf[0]) };
use_namebuf = (use_namebuf + 1) % ARRAY_SIZE(namebuf);
const char * const *pos;
sb.buf[0] = '\0';
for (pos = strs; *pos != NULL; pos++)
OSMO_STRBUF_PRINTF(sb, " %s", *pos);
return sb.buf;
}
static bool validate_sdp(const char *func, const char *desc,
const char *sdp_str, const char * const expected_codecs[])
{
const char * const *expect_pos;
struct sdp_audio_codec *codec;
struct sdp_msg sdp;
if (sdp_msg_from_sdp_str(&sdp, sdp_str)) {
BTW("%s: %s: ERROR: failed to parse SDP\n%s", func, desc, sdp_str);
return false;
}
expect_pos = expected_codecs;
foreach_sdp_audio_codec(codec, &sdp.audio_codecs) {
if (!*expect_pos) {
BTW("%s: %s: ERROR: did not expect %s", func, desc, codec->subtype_name);
return false;
}
if (strcmp(*expect_pos, codec->subtype_name)) {
BTW("%s: %s: ERROR: mismatch: in idx %d, expect %s, got %s", func, desc,
(int)(expect_pos - expected_codecs), *expect_pos, codec->subtype_name);
return false;
}
expect_pos++;
}
if (*expect_pos) {
BTW("%s: %s: ERROR: mismatch: expected %s to be listed, but not found", func, desc, *expect_pos);
return false;
}
return true;
}
#define VALIDATE_SDP(GOT_SDP_STR, EXPECT_SDP_STR) do { \
if (validate_sdp(__func__, t->desc, GOT_SDP_STR, EXPECT_SDP_STR)) { \
btw("VALIDATE_SDP OK: " #GOT_SDP_STR " == " #EXPECT_SDP_STR " ==%s", strlist_name(EXPECT_SDP_STR)); \
} else { \
btw("Failed to validate SDP:\nexpected%s\ngot\n%s", \
strlist_name(EXPECT_SDP_STR), GOT_SDP_STR); \
OSMO_ASSERT(false); \
} \
} while (0)
static bool validate_perm_speech(const char *func, const char *desc,
const struct gsm0808_channel_type *ct,
const enum gsm0808_permitted_speech perm_speech[])
{
const enum gsm0808_permitted_speech *pos;
const uint8_t *pos2 = ct->perm_spch;
for (pos = perm_speech; *pos != LIST_END; pos++, pos2++) {
if (pos2 - ct->perm_spch >= ct->perm_spch_len) {
BTW("%s: %s: ERROR: mismatch: expected %s to be listed, but not found", func, desc,
gsm0808_permitted_speech_name(*pos));
return false;
}
if (*pos2 != *pos) {
BTW("%s: %s: ERROR: mismatch: in idx %d, expect %s", func, desc,
(int)(pos - perm_speech), gsm0808_permitted_speech_name(*pos));
btw("in idx %d, got %s", (int)(pos - perm_speech), gsm0808_permitted_speech_name(*pos2));
return false;
}
}
if (pos2 - ct->perm_spch < ct->perm_spch_len) {
BTW("%s: %s: ERROR: did not expect %s", func, desc, gsm0808_permitted_speech_name(*pos2));
return false;
}
return true;
}
#define VALIDATE_PERM_SPEECH(GOT_PERM_SPEECH, EXPECT_PERM_SPEECH) do { \
if (validate_perm_speech(__func__, t->desc, GOT_PERM_SPEECH, EXPECT_PERM_SPEECH)) { \
btw("VALIDATE_PERM_SPEECH OK: " #GOT_PERM_SPEECH " == " #EXPECT_PERM_SPEECH " ==%s", \
perm_speech_name(EXPECT_PERM_SPEECH)); \
} else { \
btw("Failed to validate Permitted Speech:\nexpected%s", \
perm_speech_name(EXPECT_PERM_SPEECH)); \
btw("got:"); \
int i; \
for (i = 0; i < (GOT_PERM_SPEECH)->perm_spch_len; i++) { \
btw("%s", gsm0808_permitted_speech_name((GOT_PERM_SPEECH)->perm_spch[i])); \
}\
OSMO_ASSERT(false); \
} \
} while (0)
static struct sdp_msg *sdp_from_subtype_names(const char *const *subtype_names)
{
static struct sdp_msg sdp;
sdp = (struct sdp_msg){};
const char *const *subtype_name;
osmo_sockaddr_str_from_str(&sdp.rtp, "1.2.3.4", 56);
for (subtype_name = subtype_names; *subtype_name; subtype_name++) {
const struct codec_mapping *m = codec_mapping_by_subtype_name(*subtype_name);
if (!m) {
BTW("ERROR: unknown subtype_name: %s", *subtype_name);
abort();
}
sdp_audio_codecs_add_copy(&sdp.audio_codecs, &m->sdp);
}
return &sdp;
}
static int sdp_str_from_subtype_names(char *buf, size_t buflen, const char *const *subtype_names)
{
if (!subtype_names[0]) {
buf[0] = '\0';
return 0;
}
return sdp_msg_to_sdp_str_buf(buf, buflen, sdp_from_subtype_names(subtype_names));
}
static const char *bcap_hexstr(const enum gsm48_bcap_speech_ver ms_bcap[])
{
struct gsm_mncc_bearer_cap bcap = {
.transfer = GSM_MNCC_BCAP_SPEECH,
.speech_ver = { -1 },
.radio = GSM48_BCAP_RRQ_DUAL_FR,
};
const enum gsm48_bcap_speech_ver *pos;
for (pos = ms_bcap; *pos != LIST_END; pos++) {
bearer_cap_add_speech_ver(&bcap, *pos);
}
struct msgb *msg = msgb_alloc(128, "bcap");
gsm48_encode_bearer_cap(msg, 0, &bcap);
char *ret = osmo_hexdump_nospc(msg->data, msg->len);
msgb_free(msg);
return ret;
}
static void test_codecs_mo(const struct codec_test *t)
{
struct gsm_mncc mncc = {
.imsi = IMSI,
};
struct gsm_mncc_rtp *mncc_rtp = (void*)&mncc;
BTW("======================== MO call: %s", t->desc);
btw("CM Service Request with Codec List (BSS Supported) =%s",
codec_list_name(t->mo_rx_compl_l3_codec_list_bss_supported));
cm_service_result_sent = RES_NONE;
ms_sends_compl_l3("052471"
"03575886" /* classmark 2 */
"089910070000106005" /* IMSI */,
codec_list(t->mo_rx_compl_l3_codec_list_bss_supported));
VERBOSE_ASSERT(cm_service_result_sent, == RES_ACCEPT, "%d");
EXPECT_ACCEPTED(true);
btw("MS sends CC SETUP with Bearer Capability = %s",
bcap_name(t->mo_rx_ms_bcap));
expect_crcx(RTP_TO_CN);
expect_crcx(RTP_TO_RAN);
ms_sends_msgf("0385" /* CC, seq = 2 -> 0x80 | CC Setup = 0x5 */
"%s" /* Bearer Capability */
"5e038121f3" /* Called Number BCD */
"15020100" /* CC Capabilities */
"4008" /* Supported Codec List */
"04026000" /* UMTS: AMR 2 | AMR */
"00021f00" /* GSM: HR AMR | FR AMR | GSM EFR | GSM HR | GSM FR */,
bcap_hexstr(t->mo_rx_ms_bcap)
);
OSMO_ASSERT(crcx_scheduled(RTP_TO_CN));
OSMO_ASSERT(crcx_scheduled(RTP_TO_RAN));
btw("As soon as the MGW port towards CN is created, MNCC_SETUP_IND is triggered");
cc_to_mncc_expect_tx(IMSI, MNCC_SETUP_IND);
crcx_ok(RTP_TO_CN);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
mncc.callref = cc_to_mncc_tx_got_callref;
VALIDATE_SDP(cc_to_mncc_tx_last_sdp, t->mo_tx_sdp_mncc_setup_ind);
btw("MNCC replies with MNCC_RTP_CREATE, causing MGW endpoint CRCX to RAN");
sdp_str_from_subtype_names(mncc_rtp->sdp, sizeof(mncc_rtp->sdp), t->mo_rx_sdp_mncc_rtp_create);
mncc_sends_to_cc(MNCC_RTP_CREATE, &mncc);
btw("MGW acknowledges the CRCX, triggering Assignment with%s", perm_speech_name(t->mo_tx_assignment_perm_speech));
expect_bssap_assignment();
crcx_ok(RTP_TO_RAN);
OSMO_ASSERT(bssap_assignment_sent);
VALIDATE_PERM_SPEECH(&bssap_assignment_command_last_channel_type, t->mo_tx_assignment_perm_speech);
btw("Assignment succeeds, triggering MNCC_RTP_CREATE ack to MNCC");
cc_to_mncc_expect_tx("", MNCC_RTP_CREATE);
ms_sends_assignment_complete(t->mo_rx_assigned_codec);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
VALIDATE_SDP(cc_to_mncc_tx_last_sdp, t->mo_tx_sdp_mncc_rtp_create);
btw("MNCC says that's fine");
dtap_expect_tx("8302" /* CC: Call Proceeding */);
mncc_sends_to_cc(MNCC_CALL_PROC_REQ, &mncc);
OSMO_ASSERT(dtap_tx_confirmed);
fake_time_passes(1, 23);
btw("The other call leg got established (not shown here), MNCC tells us so, with codecs %s",
strlist_name(t->mo_rx_sdp_mncc_alert_req));
dtap_expect_tx("8301" /* CC: Call Alerting */);
sdp_str_from_subtype_names(mncc.sdp, sizeof(mncc.sdp), t->mo_rx_sdp_mncc_alert_req);
mncc_sends_to_cc(MNCC_ALERT_REQ, &mncc);
OSMO_ASSERT(dtap_tx_confirmed);
dtap_expect_tx("8307" /* CC: Connect */);
sdp_str_from_subtype_names(mncc.sdp, sizeof(mncc.sdp), t->mo_rx_sdp_mncc_setup_rsp);
mncc_sends_to_cc(MNCC_SETUP_RSP, &mncc);
OSMO_ASSERT(dtap_tx_confirmed);
fake_time_passes(1, 23);
cc_to_mncc_expect_tx("", MNCC_SETUP_COMPL_IND);
ms_sends_msg("03cf" /* CC: Connect Acknowledge */);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
VALIDATE_SDP(cc_to_mncc_tx_last_sdp, t->mo_tx_sdp_mncc_setup_compl_ind);
BTW("RTP stream goes ahead, not shown here.");
fake_time_passes(123, 45);
BTW("Call ends");
cc_to_mncc_expect_tx("", MNCC_DISC_IND);
ms_sends_msg("032502e090" /* CC: Disconnect, cause: Normal Call Clearing */);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
dtap_expect_tx("832d" /* CC: Release */);
mncc_sends_to_cc(MNCC_REL_REQ, &mncc);
OSMO_ASSERT(dtap_tx_confirmed);
cc_to_mncc_expect_tx("", MNCC_REL_CNF);
expect_bssap_clear();
ms_sends_msg("036a" /* CC: Release Complete */);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
OSMO_ASSERT(bssap_clear_sent);
ran_sends_clear_complete();
EXPECT_CONN_COUNT(0);
BTW("======================== SUCCESS: MO call: %s", t->desc);
}
static void test_codecs_mt(const struct codec_test *t)
{
struct gsm_mncc mncc = {
.imsi = IMSI,
.callref = 0x423,
.fields = MNCC_F_BEARER_CAP,
.bearer_cap = {
.speech_ver = { GSM48_BCAP_SV_FR, -1, },
},
};
struct gsm_mncc_rtp *mncc_rtp = (void*)&mncc;
BTW("======================== MT call: %s", t->desc);
BTW("MNCC asks us to setup a call, causing Paging");
paging_expect_imsi(IMSI);
paging_sent = false;
sdp_str_from_subtype_names(mncc.sdp, sizeof(mncc.sdp), t->mt_rx_sdp_mncc_setup_req);
mncc_sends_to_cc(MNCC_SETUP_REQ, &mncc);
mncc.sdp[0] = '\0';
VERBOSE_ASSERT(paging_sent, == true, "%d");
btw("MS replies with Paging Response, with Codec List (BSS Supported) =%s",
codec_list_name(t->mt_rx_compl_l3_codec_list_bss_supported));
if (t->expect_codec_mismatch_on_paging_response) {
btw("VLR accepts, but MSC notices a codec mismatch and aborts");
cc_to_mncc_expect_tx("", MNCC_REL_IND);
dtap_expect_tx("032d0802e1af" /* CC Release */);
expect_bssap_clear();
ms_sends_compl_l3("062707"
"03575886" /* classmark 2 */
"089910070000106005" /* IMSI */,
codec_list(t->mt_rx_compl_l3_codec_list_bss_supported));
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
OSMO_ASSERT(bssap_clear_sent);
ran_sends_clear_complete();
EXPECT_CONN_COUNT(0);
BTW("======================== SUCCESS: MT call: %s", t->desc);
return;
}
btw("VLR accepts, MSC sends CC Setup with Bearer Capability = %s",
bcap_name(t->mt_tx_cc_setup_bcap));
char *cc_setup_bcap = talloc_asprintf(msc_vlr_tests_ctx, "0305%s",
bcap_hexstr(t->mt_tx_cc_setup_bcap));
dtap_expect_tx(cc_setup_bcap);
ms_sends_compl_l3("062707"
"03575886" /* classmark 2 */
"089910070000106005" /* IMSI */,
codec_list(t->mt_rx_compl_l3_codec_list_bss_supported));
OSMO_ASSERT(dtap_tx_confirmed);
talloc_free(cc_setup_bcap);
btw("MS confirms call, we create a RAN-side RTP and forward MNCC_CALL_CONF_IND");
expect_crcx(RTP_TO_CN);
expect_crcx(RTP_TO_RAN);
cc_to_mncc_expect_tx(IMSI, MNCC_CALL_CONF_IND);
ms_sends_msgf("8348" /* CC: Call Confirmed */
"%s" /* Bearer Capability */
"15020100" /* Call Control Capabilities */
"40080402600400021f00" /* Supported Codec List */,
bcap_hexstr(t->mt_rx_ms_bcap)
);
OSMO_ASSERT(crcx_scheduled(RTP_TO_CN));
OSMO_ASSERT(crcx_scheduled(RTP_TO_RAN));
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
VALIDATE_SDP(cc_to_mncc_tx_last_sdp, t->mt_tx_sdp_mncc_call_conf_ind);
btw("MGW acknowledges the CRCX to RAN, triggering Assignment with%s", perm_speech_name(t->mt_tx_assignment_perm_speech));
if (t->expect_codec_mismatch_on_cc_call_conf) {
btw("MS Bearer Capability leads to a codec mismatch, Assignment aborts");
dtap_expect_tx("032d0802e1af" /* CC Release */);
cc_to_mncc_expect_tx("", MNCC_REL_IND);
expect_bssap_clear();
crcx_ok(RTP_TO_RAN);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
OSMO_ASSERT(bssap_clear_sent);
ran_sends_clear_complete();
EXPECT_CONN_COUNT(0);
BTW("======================== SUCCESS: MT call: %s", t->desc);
return;
}
expect_bssap_assignment();
crcx_ok(RTP_TO_RAN);
OSMO_ASSERT(bssap_assignment_sent);
VALIDATE_PERM_SPEECH(&bssap_assignment_command_last_channel_type, t->mt_tx_assignment_perm_speech);
btw("Assignment completes, triggering CRCX to CN");
ms_sends_assignment_complete(t->mt_rx_assigned_codec);
btw("MNCC sends MNCC_RTP_CREATE, which first waits for the CN side RTP");
sdp_str_from_subtype_names(mncc_rtp->sdp, sizeof(mncc_rtp->sdp), t->mt_rx_sdp_mncc_rtp_create);
mncc_sends_to_cc(MNCC_RTP_CREATE, &mncc);
btw("When the CN side RTP address is known, ack MNCC_RTP_CREATE");
cc_to_mncc_expect_tx("", MNCC_RTP_CREATE);
crcx_ok(RTP_TO_CN);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
VALIDATE_SDP(cc_to_mncc_tx_last_sdp, t->mt_tx_sdp_mncc_rtp_create);
fake_time_passes(1, 23);
cc_to_mncc_expect_tx("", MNCC_ALERT_IND);
ms_sends_msg("8381" /* CC: Alerting */);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
VALIDATE_SDP(cc_to_mncc_tx_last_sdp, t->mt_tx_sdp_mncc_alert_ind);
fake_time_passes(1, 23);
cc_to_mncc_expect_tx(IMSI, MNCC_SETUP_CNF);
ms_sends_msg("83c7" /* CC: Connect */);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
VALIDATE_SDP(cc_to_mncc_tx_last_sdp, t->mt_tx_sdp_mncc_setup_cnf);
dtap_expect_tx("030f" /* CC: Connect Acknowledge */);
sdp_str_from_subtype_names(mncc.sdp, sizeof(mncc.sdp), t->mt_rx_sdp_mncc_setup_compl_req);
mncc_sends_to_cc(MNCC_SETUP_COMPL_REQ, &mncc);
BTW("RTP stream goes ahead, not shown here.");
fake_time_passes(123, 45);
BTW("Call ends");
cc_to_mncc_expect_tx("", MNCC_DISC_IND);
ms_sends_msg("832502e090" /* CC: Disconnect, cause: Normal Call Clearing */);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
dtap_expect_tx("032d" /* CC: Release */);
mncc_sends_to_cc(MNCC_REL_REQ, &mncc);
OSMO_ASSERT(dtap_tx_confirmed);
cc_to_mncc_expect_tx("", MNCC_REL_CNF);
expect_bssap_clear();
ms_sends_msg("836a" /* CC: Release Complete */);
OSMO_ASSERT(cc_to_mncc_tx_confirmed);
OSMO_ASSERT(bssap_clear_sent);
ran_sends_clear_complete();
EXPECT_CONN_COUNT(0);
BTW("======================== SUCCESS: MT call: %s", t->desc);
}
static void test_codecs()
{
const struct codec_test *t;
clear_vlr();
comment_start();
fake_time_start();
lu_geran_noauth();
for (t = codec_tests; t - codec_tests < ARRAY_SIZE(codec_tests); t++) {
test_codecs_mo(t);
test_codecs_mt(t);
}
EXPECT_CONN_COUNT(0);
clear_vlr();
comment_end();
}
msc_vlr_test_func_t msc_vlr_tests[] = { msc_vlr_test_func_t msc_vlr_tests[] = {
test_call_mo, test_call_mo,
@ -716,5 +1443,6 @@ msc_vlr_test_func_t msc_vlr_tests[] = {
test_call_mt2, test_call_mt2,
test_call_mo_to_unknown, test_call_mo_to_unknown,
test_call_mo_to_unknown_timeout, test_call_mo_to_unknown_timeout,
test_codecs,
NULL NULL
}; };

File diff suppressed because it is too large Load Diff

View File

@ -77,6 +77,7 @@ bool bssap_clear_sent = false;
bool bssap_assignment_expected = false; bool bssap_assignment_expected = false;
bool bssap_assignment_sent = false; bool bssap_assignment_sent = false;
struct gsm0808_channel_type bssap_assignment_command_last_channel_type;
bool iu_rab_assignment_expected = false; bool iu_rab_assignment_expected = false;
bool iu_rab_assignment_sent = false; bool iu_rab_assignment_sent = false;
@ -84,6 +85,7 @@ uint32_t cc_to_mncc_tx_expected_msg_type = 0;
const char *cc_to_mncc_tx_expected_imsi = NULL; const char *cc_to_mncc_tx_expected_imsi = NULL;
bool cc_to_mncc_tx_confirmed = false; bool cc_to_mncc_tx_confirmed = false;
uint32_t cc_to_mncc_tx_got_callref = 0; uint32_t cc_to_mncc_tx_got_callref = 0;
char cc_to_mncc_tx_last_sdp[1024] = {};
bool expecting_crcx[2] = {}; bool expecting_crcx[2] = {};
bool got_crcx[2] = {}; bool got_crcx[2] = {};
@ -308,14 +310,18 @@ static int bssap_validate_cipher_mode_cmd(const struct ran_cipher_mode_command *
return 0; return 0;
} }
static void bssap_validate_assignment_cmd() static void bssap_validate_assignment_cmd(const struct ran_assignment_command *assignment_command)
{ {
OSMO_ASSERT(bssap_assignment_expected); OSMO_ASSERT(bssap_assignment_expected);
bssap_assignment_expected = false; bssap_assignment_expected = false;
bssap_assignment_sent = true; bssap_assignment_sent = true;
if (assignment_command->channel_type)
bssap_assignment_command_last_channel_type = *assignment_command->channel_type;
else
bssap_assignment_command_last_channel_type = (struct gsm0808_channel_type){};
} }
static void iucs_validate_assignment_cmd() static void iucs_validate_assignment_cmd(const struct ran_assignment_command *assignment_command)
{ {
OSMO_ASSERT(iu_rab_assignment_expected); OSMO_ASSERT(iu_rab_assignment_expected);
iu_rab_assignment_expected = false; iu_rab_assignment_expected = false;
@ -375,10 +381,10 @@ struct msgb *dont_ran_encode(struct osmo_fsm_inst *caller_fi, const struct ran_m
case RAN_MSG_ASSIGNMENT_COMMAND: case RAN_MSG_ASSIGNMENT_COMMAND:
switch (ran_type) { switch (ran_type) {
case OSMO_RAT_GERAN_A: case OSMO_RAT_GERAN_A:
bssap_validate_assignment_cmd(); bssap_validate_assignment_cmd(&ran_enc_msg->assignment_command);
break; break;
case OSMO_RAT_UTRAN_IU: case OSMO_RAT_UTRAN_IU:
iucs_validate_assignment_cmd(); iucs_validate_assignment_cmd(&ran_enc_msg->assignment_command);
break; break;
default: default:
OSMO_ASSERT(false); OSMO_ASSERT(false);
@ -420,7 +426,7 @@ static int fake_msc_a_ran_dec(const struct ran_msg *ran_dec_msg)
return msc_a_ran_decode_cb(g_msub->role[MSC_ROLE_A], &d, ran_dec_msg); return msc_a_ran_decode_cb(g_msub->role[MSC_ROLE_A], &d, ran_dec_msg);
} }
void rx_from_ms(struct msgb *msg) void rx_from_ms(struct msgb *msg, const struct gsm0808_speech_codec_list *codec_list_bss_supported)
{ {
struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_hdr *gh = msgb_l3(msg);
struct ran_msg ran_dec_msg; struct ran_msg ran_dec_msg;
@ -453,6 +459,7 @@ void rx_from_ms(struct msgb *msg)
.compl_l3 = { .compl_l3 = {
.cell_id = &cell_id, .cell_id = &cell_id,
.msg = msg, .msg = msg,
.codec_list_bss_supported = codec_list_bss_supported,
}, },
}; };
} else { } else {
@ -480,7 +487,30 @@ void ms_sends_msg(const char *hex)
msg = msgb_from_hex("ms_sends_msg", 1024, hex); msg = msgb_from_hex("ms_sends_msg", 1024, hex);
msg->l1h = msg->l2h = msg->l3h = msg->data; msg->l1h = msg->l2h = msg->l3h = msg->data;
rx_from_ms(msg); rx_from_ms(msg, NULL);
msgb_free(msg);
}
void ms_sends_msgf(const char *fmt, ...)
{
va_list ap;
char *hex;
va_start(ap, fmt);
hex = talloc_vasprintf(msc_vlr_tests_ctx, fmt, ap);
va_end(ap);
ms_sends_msg(hex);
talloc_free(hex);
}
void ms_sends_compl_l3(const char *hex, const struct gsm0808_speech_codec_list *codec_list_bss_supported)
{
struct msgb *msg;
msg = msgb_from_hex("ms_sends_msg", 1024, hex);
msg->l1h = msg->l2h = msg->l3h = msg->data;
rx_from_ms(msg, codec_list_bss_supported);
msgb_free(msg); msgb_free(msg);
} }
@ -723,8 +753,18 @@ struct gsm_mncc *on_call_release_mncc_sends_to_cc_data = NULL;
int mncc_recv(struct gsm_network *net, struct msgb *msg) int mncc_recv(struct gsm_network *net, struct msgb *msg)
{ {
struct gsm_mncc *mncc = (void*)msg->data; struct gsm_mncc *mncc = (void*)msg->data;
log("MSC --> MNCC: callref 0x%x: %s", mncc->callref, if (mncc->msg_type == MNCC_RTP_CREATE) {
get_mncc_name(mncc->msg_type)); struct gsm_mncc_rtp *rtp = (void*)msg->data;
log("MSC --> MNCC: callref 0x%x: %s\n%s", rtp->callref,
get_mncc_name(rtp->msg_type),
rtp->sdp);
OSMO_STRLCPY_ARRAY(cc_to_mncc_tx_last_sdp, rtp->sdp);
} else {
log("MSC --> MNCC: callref 0x%x: %s\n%s", mncc->callref,
get_mncc_name(mncc->msg_type),
mncc->sdp);
OSMO_STRLCPY_ARRAY(cc_to_mncc_tx_last_sdp, mncc->sdp);
}
if (mncc->msg_type == MNCC_REL_IND && on_call_release_mncc_sends_to_cc_data) { if (mncc->msg_type == MNCC_REL_IND && on_call_release_mncc_sends_to_cc_data) {
@ -1009,9 +1049,12 @@ void ms_sends_security_mode_complete(uint8_t utran_encryption)
g_msub = NULL; g_msub = NULL;
} }
void ms_sends_assignment_complete(enum mgcp_codecs assigned_codec) void ms_sends_assignment_complete(const char *sdp_codec_name)
{ {
struct ran_msg ran_dec; struct ran_msg ran_dec;
const struct codec_mapping *m = codec_mapping_by_subtype_name(sdp_codec_name);
OSMO_ASSERT(m);
enum mgcp_codecs assigned_codec = m->mgcp;
ran_dec = (struct ran_msg){ ran_dec = (struct ran_msg){
.msg_type = RAN_MSG_ASSIGNMENT_COMPLETE, .msg_type = RAN_MSG_ASSIGNMENT_COMPLETE,

View File

@ -56,6 +56,8 @@ extern bool _log_lines;
#define comment_start() fprintf(stderr, "===== %s\n", __func__); #define comment_start() fprintf(stderr, "===== %s\n", __func__);
#define comment_end() fprintf(stderr, "===== %s: SUCCESS\n\n", __func__); #define comment_end() fprintf(stderr, "===== %s: SUCCESS\n\n", __func__);
extern void *msc_vlr_tests_ctx;
extern struct msub *g_msub; extern struct msub *g_msub;
extern struct gsm_network *net; extern struct gsm_network *net;
extern void *msgb_ctx; extern void *msgb_ctx;
@ -116,6 +118,7 @@ extern uint32_t cc_to_mncc_tx_expected_msg_type;
extern const char *cc_to_mncc_tx_expected_imsi; extern const char *cc_to_mncc_tx_expected_imsi;
extern bool cc_to_mncc_tx_confirmed; extern bool cc_to_mncc_tx_confirmed;
extern uint32_t cc_to_mncc_tx_got_callref; extern uint32_t cc_to_mncc_tx_got_callref;
extern char cc_to_mncc_tx_last_sdp[1024];
extern struct gsm_mncc *on_call_release_mncc_sends_to_cc_data; extern struct gsm_mncc *on_call_release_mncc_sends_to_cc_data;
@ -148,6 +151,7 @@ static inline void expect_release_clear(enum osmo_rat_type via_ran)
extern bool bssap_assignment_expected; extern bool bssap_assignment_expected;
extern bool bssap_assignment_sent; extern bool bssap_assignment_sent;
extern struct gsm0808_channel_type bssap_assignment_command_last_channel_type;
extern bool iu_rab_assignment_expected; extern bool iu_rab_assignment_expected;
extern bool iu_rab_assignment_sent; extern bool iu_rab_assignment_sent;
@ -183,10 +187,12 @@ void paging_expect_imsi(const char *imsi);
void paging_expect_tmsi(uint32_t tmsi); void paging_expect_tmsi(uint32_t tmsi);
void ms_sends_msg(const char *hex); void ms_sends_msg(const char *hex);
void ms_sends_msgf(const char *fmt, ...);
void ms_sends_compl_l3(const char *hex, const struct gsm0808_speech_codec_list *codec_list_bss_supported);
void ms_sends_classmark_update(const struct osmo_gsm48_classmark *classmark); void ms_sends_classmark_update(const struct osmo_gsm48_classmark *classmark);
void ms_sends_ciphering_mode_complete(const char *inner_nas_msg); void ms_sends_ciphering_mode_complete(const char *inner_nas_msg);
void ms_sends_security_mode_complete(uint8_t utran_encryption); void ms_sends_security_mode_complete(uint8_t utran_encryption);
void ms_sends_assignment_complete(enum mgcp_codecs assigned_codec); void ms_sends_assignment_complete(const char *sdp_codec_name);
void gsup_rx(const char *rx_hex, const char *expect_tx_hex); void gsup_rx(const char *rx_hex, const char *expect_tx_hex);
void send_sms(struct vlr_subscr *receiver, void send_sms(struct vlr_subscr *receiver,
struct vlr_subscr *sender, struct vlr_subscr *sender,