implement CM Re-Establish for voice calls

Related: SYS#5130
Change-Id: I6fa37d6ca9fcb1637742b40e37b68d67664c9b60
This commit is contained in:
Neels Hofmeyr 2021-07-27 03:46:49 +02:00
parent 5bdba0d48d
commit ae98b97382
5 changed files with 136 additions and 2 deletions

View File

@ -44,6 +44,8 @@ enum {
MSC_CTR_CM_SERVICE_REQUEST_ACCEPTED,
MSC_CTR_PAGING_RESP_REJECTED,
MSC_CTR_PAGING_RESP_ACCEPTED,
MSC_CTR_CM_RE_ESTABLISH_REQ_REJECTED,
MSC_CTR_CM_RE_ESTABLISH_REQ_ACCEPTED,
MSC_CTR_SMS_SUBMITTED,
MSC_CTR_SMS_NO_RECEIVER,
MSC_CTR_SMS_DELIVERED,
@ -76,6 +78,8 @@ static const struct rate_ctr_desc msc_ctr_description[] = {
[MSC_CTR_CM_SERVICE_REQUEST_ACCEPTED] = {"cm_service_request:accepted", "Accepted CM Service Requests."},
[MSC_CTR_PAGING_RESP_REJECTED] = {"paging_resp:rejected", "Rejected Paging Responses."},
[MSC_CTR_PAGING_RESP_ACCEPTED] = {"paging_resp:accepted", "Accepted Paging Responses."},
[MSC_CTR_CM_RE_ESTABLISH_REQ_REJECTED] = {"cm_re_establish_request:rejected", "Rejected CM Re-Establishing Requests."},
[MSC_CTR_CM_RE_ESTABLISH_REQ_ACCEPTED] = {"cm_re_establish_request:accepted", "Accepted CM Re-Establishing Requests."},
[MSC_CTR_SMS_SUBMITTED] = {"sms:submitted", "Total MO SMS received from the MS."},
[MSC_CTR_SMS_NO_RECEIVER] = {"sms:no_receiver", "Failed MO SMS delivery attempts (no receiver found)."},
[MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR] = {"sms:deliver_unknown_error", "Failed MO SMS delivery attempts (other reason)."},

View File

@ -41,6 +41,7 @@ enum complete_layer3_type {
COMPLETE_LAYER3_LU,
COMPLETE_LAYER3_CM_SERVICE_REQ,
COMPLETE_LAYER3_PAGING_RESP,
COMPLETE_LAYER3_CM_RE_ESTABLISH_REQ,
};
extern const struct value_string complete_layer3_type_names[];

View File

@ -444,6 +444,7 @@ enum vlr_parq_type {
VLR_PR_ARQ_T_INVALID = 0, /* to guard against unset vars */
VLR_PR_ARQ_T_CM_SERV_REQ,
VLR_PR_ARQ_T_PAGING_RESP,
VLR_PR_ARQ_T_CM_RE_ESTABLISH_REQ,
/* FIXME: differentiate between services of 24.008 10.5.3.3 */
};

View File

@ -48,11 +48,13 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/core/fsm.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/crypt/auth.h>
#include <osmocom/msc/msub.h>
#include <osmocom/msc/msc_roles.h>
#include <osmocom/msc/call_leg.h>
#include <assert.h>
@ -824,17 +826,128 @@ int gsm48_rx_mm_serv_req(struct msc_a *msc_a, struct msgb *msg)
/* Receive a CM Re-establish Request */
static int gsm48_rx_cm_reest_req(struct msc_a *msc_a, struct msgb *msg)
{
struct gsm_network *net = msc_a_net(msc_a);
struct gsm48_hdr *gh;
struct gsm48_service_request *req;
struct gsm48_classmark2 *cm2;
uint8_t *cm2_buf, cm2_len;
bool is_utran;
struct vlr_subscr *vsub;
struct osmo_mobile_identity mi;
struct msub *prev_msub;
struct msc_a *prev_msc_a;
int rc = osmo_mobile_identity_decode_from_l3(&mi, msg, false);
if (rc) {
LOGP(DMM, LOGL_ERROR, "CM RE-ESTABLISH REQUEST: cannot decode Mobile Identity\n");
return -EINVAL;
}
msc_a->complete_layer3_type = COMPLETE_LAYER3_CM_RE_ESTABLISH_REQ;
msub_update_id_from_mi(msc_a->c.msub, &mi);
DEBUGP(DMM, "<- CM RE-ESTABLISH REQUEST %s\n", osmo_mobile_identity_to_str_c(OTC_SELECT, &mi));
/* we don't support CM call re-establishment */
return msc_gsm48_tx_mm_serv_rej(msc_a, GSM48_REJECT_SRV_OPT_NOT_SUPPORTED);
gh = (struct gsm48_hdr *) msgb_l3(msg);
req = (struct gsm48_service_request *) gh->data;
/* Unfortunately in Phase1 the Classmark2 length is variable, so we cannot
* just use gsm48_service_request struct, and need to parse it manually. */
cm2_len = gh->data[1];
cm2_buf = gh->data + 2;
cm2 = (struct gsm48_classmark2 *) cm2_buf;
/* Prevent buffer overrun: check the length of Classmark2 */
if (cm2_buf + cm2_len > msg->tail) {
LOG_MSC_A(msc_a, LOGL_ERROR, "Rx CM SERVICE REQUEST: Classmark2 "
"length=%u is too big\n", cm2_len);
return msc_gsm48_tx_mm_serv_rej(msc_a, GSM48_REJECT_INCORRECT_MESSAGE);
}
/* Look up the other, previously active connection for this subscriber */
vsub = vlr_subscr_find_by_mi(net->vlr, &mi, __func__);
prev_msub = msub_for_vsub(vsub);
prev_msc_a = msub_msc_a(prev_msub);
if (!vsub || !prev_msub || !prev_msc_a) {
LOG_MSC_A(msc_a, LOGL_ERROR, "CM Re-Establish Request for unknown subscriber: %s\n",
osmo_mobile_identity_to_str_c(OTC_SELECT, &mi));
if (vsub)
vlr_subscr_put(vsub, __func__);
return msc_gsm48_tx_mm_serv_rej(msc_a, GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED);
}
LOG_MSC_A(msc_a, LOGL_NOTICE, "New conn requesting Re-Establishment\n");
LOG_MSC_A(prev_msc_a, LOGL_NOTICE, "Old conn matching Re-Establishment request (%s)\n",
osmo_use_count_to_str_c(OTC_SELECT, &prev_msc_a->use_count));
if (!prev_msc_a->cc.call_leg || !prev_msc_a->cc.active_trans) {
LOG_MSC_A(msc_a, LOGL_ERROR, "CM Re-Establish Request only supported for voice calls\n");
if (vsub)
vlr_subscr_put(vsub, __func__);
return msc_gsm48_tx_mm_serv_rej(msc_a, GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED);
}
msc_a_get(prev_msc_a, __func__);
/* Move the call_leg and active CC trans over to the new msc_a */
call_leg_reparent(prev_msc_a->cc.call_leg,
msc_a->c.fi,
MSC_EV_CALL_LEG_TERM,
MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE,
MSC_EV_CALL_LEG_RTP_COMPLETE);
msc_a->cc.call_leg = prev_msc_a->cc.call_leg;
prev_msc_a->cc.call_leg = NULL;
msc_a->cc.active_trans = prev_msc_a->cc.active_trans;
msc_a->cc.active_trans->msc_a = msc_a;
msc_a_get(msc_a, MSC_A_USE_CC);
prev_msc_a->cc.active_trans = NULL;
msc_a_put(prev_msc_a, MSC_A_USE_CC);
/* Dis-associate the VLR subscriber from the previous msc_a, so that we can start a new Process Access Request
* on the new msc_a. */
if (vsub->proc_arq_fsm) {
osmo_fsm_inst_term(vsub->proc_arq_fsm, OSMO_FSM_TERM_REGULAR, NULL);
vsub->proc_arq_fsm = NULL;
}
if (prev_msub->vsub) {
vlr_subscr_put(prev_msub->vsub, VSUB_USE_MSUB);
prev_msub->vsub = NULL;
}
/* Clear the previous conn.
* FIXME: we are clearing the previous conn before having authenticated the new conn. That means anyone can send
* CM Re-Establishing requests with arbitrary mobile identities without having to authenticate, and can freely
* Clear any connections at will. */
msc_a_release_cn(prev_msc_a);
msc_a_put(prev_msc_a, __func__);
prev_msc_a = NULL;
/* Kick off Authentication and Ciphering for the new conn. */
is_utran = (msc_a->c.ran->type == OSMO_RAT_UTRAN_IU);
vlr_proc_acc_req(msc_a->c.fi,
MSC_A_EV_AUTHENTICATED, MSC_A_EV_CN_CLOSE, NULL,
net->vlr, msc_a,
VLR_PR_ARQ_T_CM_RE_ESTABLISH_REQ, 0,
&mi, &msc_a->via_cell.lai,
is_utran || net->authentication_required,
is_utran ? net->uea_encryption : net->a5_encryption_mask > 0x01,
req->cipher_key_seq,
osmo_gsm48_classmark2_is_r99(cm2, cm2_len),
is_utran);
vlr_subscr_put(vsub, __func__);
/* From vlr_proc_acc_req() we expect an implicit dispatch of PR_ARQ_E_START, and we expect
* msc_vlr_subscr_assoc() to already have been called and completed. Has an error occurred? */
vsub = msc_a_vsub(msc_a);
if (!vsub) {
LOG_MSC_A(msc_a, LOGL_ERROR, "subscriber not allowed to do a CM Service Request\n");
return -EIO;
}
vsub->classmark.classmark2 = *cm2;
vsub->classmark.classmark2_len = cm2_len;
return 0;
}
static int gsm48_rx_mm_imsi_detach_ind(struct msc_a *msc_a, struct msgb *msg)

View File

@ -120,6 +120,11 @@ static void update_counters(struct osmo_fsm_inst *fi, bool conn_accepted)
case COMPLETE_LAYER3_PAGING_RESP:
rate_ctr_inc(rate_ctr_group_get_ctr(net->msc_ctrs, conn_accepted ? MSC_CTR_PAGING_RESP_ACCEPTED : MSC_CTR_PAGING_RESP_REJECTED));
break;
case COMPLETE_LAYER3_CM_RE_ESTABLISH_REQ:
rate_ctr_inc(rate_ctr_group_get_ctr(net->msc_ctrs,
conn_accepted ? MSC_CTR_CM_RE_ESTABLISH_REQ_ACCEPTED
: MSC_CTR_CM_RE_ESTABLISH_REQ_REJECTED));
break;
default:
break;
}
@ -151,6 +156,15 @@ static void evaluate_acceptance_outcome(struct osmo_fsm_inst *fi, bool conn_acce
if (msc_a->complete_layer3_type == COMPLETE_LAYER3_LU)
msc_a_put(msc_a, MSC_A_USE_LOCATION_UPDATING);
if (msc_a->complete_layer3_type == COMPLETE_LAYER3_CM_RE_ESTABLISH_REQ) {
/* Trigger new Assignment to recommence the voice call. A little dance here because normally we verify
* that no CC trans is already active. */
struct gsm_trans *cc_trans = msc_a->cc.active_trans;
msc_a->cc.active_trans = NULL;
osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_A_EV_TRANSACTION_ACCEPTED, cc_trans);
msc_a_try_call_assignment(cc_trans);
}
}
bool msc_a_is_accepted(const struct msc_a *msc_a)
@ -1092,6 +1106,7 @@ const struct value_string complete_layer3_type_names[] = {
{ COMPLETE_LAYER3_LU, "LU" },
{ COMPLETE_LAYER3_CM_SERVICE_REQ, "CM_SERVICE_REQ" },
{ COMPLETE_LAYER3_PAGING_RESP, "PAGING_RESP" },
{ COMPLETE_LAYER3_CM_RE_ESTABLISH_REQ, "CM_RE_ESTABLISH_REQ" },
{ 0, NULL }
};