diff --git a/include/osmocom/msc/gsm_data.h b/include/osmocom/msc/gsm_data.h index 1870804d0..170d88455 100644 --- a/include/osmocom/msc/gsm_data.h +++ b/include/osmocom/msc/gsm_data.h @@ -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)."}, diff --git a/include/osmocom/msc/msc_common.h b/include/osmocom/msc/msc_common.h index e7ac559a0..f3fb0e07a 100644 --- a/include/osmocom/msc/msc_common.h +++ b/include/osmocom/msc/msc_common.h @@ -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[]; diff --git a/include/osmocom/msc/vlr.h b/include/osmocom/msc/vlr.h index 327333333..f12e90675 100644 --- a/include/osmocom/msc/vlr.h +++ b/include/osmocom/msc/vlr.h @@ -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 */ }; diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c index fb450e69a..565e7adb7 100644 --- a/src/libmsc/gsm_04_08.c +++ b/src/libmsc/gsm_04_08.c @@ -48,11 +48,13 @@ #include #include #include +#include #include #include #include #include +#include #include @@ -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) diff --git a/src/libmsc/msc_a.c b/src/libmsc/msc_a.c index 0bc7b2697..fa8e8428b 100644 --- a/src/libmsc/msc_a.c +++ b/src/libmsc/msc_a.c @@ -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 } };