osmo-msc/src/libmsc/msc_t.c

956 lines
31 KiB
C

/* The MSC-T role, a transitional RAN connection during Handover. */
/*
* (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* Author: Neels Hofmeyr
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <inttypes.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/msc/msc_t.h>
#include <osmocom/msc/msc_a.h>
#include <osmocom/msc/msc_a_remote.h>
#include <osmocom/msc/ran_infra.h>
#include <osmocom/msc/ran_peer.h>
#include <osmocom/msc/ran_conn.h>
#include <osmocom/msc/msub.h>
#include <osmocom/msc/call_leg.h>
#include <osmocom/msc/rtp_stream.h>
#include <osmocom/msc/ran_infra.h>
#include <osmocom/msc/vlr.h>
#include <osmocom/msc/msc_i.h>
#include <osmocom/msc/gsm_data.h>
static struct osmo_fsm msc_t_fsm;
static struct msc_t *msc_t_find_by_handover_number(const char *handover_number)
{
struct msub *msub;
llist_for_each_entry(msub, &msub_list, entry) {
struct msc_t *msc_t = msub_msc_t(msub);
if (!msc_t)
continue;
if (!*msc_t->inter_msc.handover_number)
continue;
if (strcmp(msc_t->inter_msc.handover_number, handover_number))
continue;
/* Found the assigned Handover Number */
return msc_t;
}
return NULL;
}
static uint64_t net_handover_number_next(struct gsm_network *net)
{
uint64_t nr;
if (net->handover_number.next < net->handover_number.range_start
|| net->handover_number.next > net->handover_number.range_end)
net->handover_number.next = net->handover_number.range_start;
nr = net->handover_number.next;
net->handover_number.next++;
return nr;
}
static int msc_t_assign_handover_number(struct msc_t *msc_t)
{
int rc;
uint64_t started_at;
uint64_t ho_nr;
char ho_nr_str[GSM23003_MSISDN_MAX_DIGITS+1];
struct gsm_network *net = msc_t_net(msc_t);
bool usable = false;
started_at = ho_nr = net_handover_number_next(net);
if (!ho_nr) {
LOG_MSC_T(msc_t, LOGL_ERROR, "No Handover Number range defined in MSC config\n");
return -ENOENT;
}
do {
rc = snprintf(ho_nr_str, sizeof(ho_nr_str), "%"PRIu64, ho_nr);
if (rc <= 0 || rc >= sizeof(ho_nr_str)) {
LOG_MSC_T(msc_t, LOGL_ERROR, "Cannot compose Handover Number string (rc=%d)\n", rc);
return -EINVAL;
}
if (!msc_t_find_by_handover_number(ho_nr_str)) {
usable = true;
break;
}
ho_nr = net_handover_number_next(net);
} while(ho_nr != started_at);
if (!usable) {
LOG_MSC_T(msc_t, LOGL_ERROR, "No Handover Number available\n");
return -EINVAL;
}
LOG_MSC_T(msc_t, LOGL_INFO, "Assigning Handover Number %s\n", ho_nr_str);
OSMO_STRLCPY_ARRAY(msc_t->inter_msc.handover_number, ho_nr_str);
return 0;
}
static struct msc_t *msc_t_priv(struct osmo_fsm_inst *fi)
{
OSMO_ASSERT(fi);
OSMO_ASSERT(fi->fsm == &msc_t_fsm);
OSMO_ASSERT(fi->priv);
return fi->priv;
}
/* As a macro to log the caller's source file and line.
* Assumes presence of local msc_t variable. */
#define msc_t_error(fmt, args...) do { \
msc_t->ho_success = false; \
LOG_MSC_T(msc_t, LOGL_ERROR, fmt, ##args); \
msc_t_clear(msc_t); \
} while(0)
static void msc_t_send_handover_failure(struct msc_t *msc_t, enum gsm0808_cause cause)
{
struct ran_msg ran_enc_msg = {
.msg_type = RAN_MSG_HANDOVER_FAILURE,
.handover_failure = {
.cause = cause,
},
};
struct an_apdu an_apdu = {
.an_proto = msc_t->c.ran->an_proto,
.msg = msc_role_ran_encode(msc_t->c.fi, &ran_enc_msg),
};
msc_t->ho_fail_sent = true;
if (!an_apdu.msg)
return;
msub_role_dispatch(msc_t->c.msub, MSC_ROLE_A, MSC_A_EV_FROM_T_PREPARE_HANDOVER_FAILURE, &an_apdu);
msgb_free(an_apdu.msg);
}
static int msc_t_ho_request_decode_and_store_cb(struct osmo_fsm_inst *msc_t_fi, void *data,
const struct ran_msg *ran_dec)
{
struct msc_t *msc_t = msc_t_priv(msc_t_fi);
if (ran_dec->msg_type != RAN_MSG_HANDOVER_REQUEST) {
LOG_MSC_T(msc_t, LOGL_DEBUG, "Expected %s in incoming inter-MSC Handover message, got %s\n",
ran_msg_type_name(RAN_MSG_HANDOVER_REQUEST), ran_msg_type_name(ran_dec->msg_type));
return -EINVAL;
}
msc_t->inter_msc.cell_id_target = ran_dec->handover_request.cell_id_target;
msc_t->inter_msc.callref = ran_dec->handover_request.call_id;
/* TODO other parameters...?
* Global Call Reference
*/
return 0;
}
/* On an icoming Handover Request from a remote MSC, we first need to set up an MGW endpoint, because the BSC needs to
* know our AoIP Transport Layer Address in the Handover Request message (which obviously the remote MSC doesn't send,
* it needs to be our local RTP address). Creating the MGW endpoint this is asynchronous, so we need to store the
* Handover Request data to forward to the BSC once the MGW endpoint is known.
*/
static int msc_t_decode_and_store_ho_request(struct msc_t *msc_t, const struct an_apdu *an_apdu)
{
if (msc_role_ran_decode(msc_t->c.fi, an_apdu, msc_t_ho_request_decode_and_store_cb, NULL)) {
msc_t_error("Failed to decode Handover Request\n");
return -ENOTSUP;
}
/* Ok, decoding done, and above msc_t_ho_request_decode_and_store_cb() has retrieved what info we need at this
* point and stored it in msc_t->inter_msc.* */
/* We're storing this for use after async events, so need to make sure that each and every bit of data is copied
* and no longer references some msgb that might be deallocated when this returns, nor remains in a local stack
* variable of some ran_decode implementation. The simplest is to store the entire msgb. */
msc_t->inter_msc.ho_request = (struct an_apdu) {
.an_proto = an_apdu->an_proto,
.msg = msgb_copy(an_apdu->msg, "saved inter-MSC Handover Request"),
/* A decoded osmo_gsup_message often still references memory of within the msgb the GSUP was received
* in. So, any info from an_apdu->e_info that would be needed would have to be copied separately.
* Omit e_info completely. */
};
return 0;
}
/* On an incoming Handover Request from a remote MSC, the target cell was transmitted in the Handover Request message.
* Find the RAN peer and assign from the cell id decoded above in msc_t_decode_and_store_ho_request(). */
static int msc_t_find_ran_peer_from_ho_request(struct msc_t *msc_t)
{
struct msc_a *msc_a = msub_msc_a(msc_t->c.msub);
const struct neighbor_ident_entry *nie;
struct ran_peer *rp_from_neighbor_ident;
struct ran_peer *rp;
switch (msc_ho_find_target_cell(msc_a, &msc_t->inter_msc.cell_id_target,
&nie, &rp_from_neighbor_ident, &rp)) {
case MSC_NEIGHBOR_TYPE_REMOTE_MSC:
msc_t_error("Incoming Handover Request indicated target cell that belongs to a remote MSC:"
" Cell ID: %s; remote MSC: %s\n",
gsm0808_cell_id_name(&msc_t->inter_msc.cell_id_target),
neighbor_ident_addr_name(&nie->addr));
return -EINVAL;
case MSC_NEIGHBOR_TYPE_NONE:
msc_t_error("Incoming Handover Request for unknown cell %s\n",
gsm0808_cell_id_name(&msc_t->inter_msc.cell_id_target));
return -EINVAL;
case MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER:
/* That's what is expected: a local RAN peer, e.g. BSC, or a remote BSC from neighbor cfg. */
if (!rp)
rp = rp_from_neighbor_ident;
break;
}
OSMO_ASSERT(rp);
LOG_MSC_T(msc_t, LOGL_DEBUG, "Incoming Handover Request indicates target cell %s,"
" which belongs to RAN peer %s\n",
gsm0808_cell_id_name(&msc_t->inter_msc.cell_id_target), rp->fi->id);
/* Finally we know where to direct the Handover */
msc_t_set_ran_peer(msc_t, rp);
return 0;
}
static int msc_t_send_stored_ho_request__decode_cb(struct osmo_fsm_inst *msc_t_fi, void *data,
const struct ran_msg *ran_dec)
{
int rc;
struct an_apdu an_apdu;
struct msc_t *msc_t = msc_t_priv(msc_t_fi);
struct osmo_sockaddr_str *rtp_ran_local = data;
/* Copy ran_dec message to un-const so we can add the AoIP Transport Layer Address. All pointer references still
* remain on the same memory as ran_dec, which is fine. We're just going to encode it again right away. */
struct ran_msg ran_enc = *ran_dec;
if (ran_dec->msg_type != RAN_MSG_HANDOVER_REQUEST) {
LOG_MSC_T(msc_t, LOGL_DEBUG, "Expected %s in incoming inter-MSC Handover message, got %s\n",
ran_msg_type_name(RAN_MSG_HANDOVER_REQUEST), ran_msg_type_name(ran_dec->msg_type));
return -EINVAL;
}
/* Insert AoIP Transport Layer Address */
ran_enc.handover_request.rtp_ran_local = rtp_ran_local;
/* Finally ready to forward to BSC: encode and send out. */
an_apdu = (struct an_apdu){
.an_proto = msc_t->inter_msc.ho_request.an_proto,
.msg = msc_role_ran_encode(msc_t->c.fi, &ran_enc),
};
if (!an_apdu.msg)
return -EIO;
rc = msc_t_down_l2_co(msc_t, &an_apdu, true);
msgb_free(an_apdu.msg);
return rc;
}
/* The MGW endpoint is created, we know our AoIP Transport Layer Address and can send the Handover Request to the RAN
* peer. */
static int msc_t_send_stored_ho_request(struct msc_t *msc_t)
{
struct osmo_sockaddr_str *rtp_ran_local = call_leg_local_ip(msc_t->inter_msc.call_leg, RTP_TO_RAN);
if (!rtp_ran_local) {
msc_t_error("Local RTP address towards RAN is not set up properly, cannot send Handover Request\n");
return -EINVAL;
}
/* The Handover Request received from the remote MSC is fed through, except we need to insert our local AoIP
* Transport Layer Address, i.e. the RTP IP:port of the MGW towards the RAN side. So we actually need to decode,
* add the AoIP and re-encode. By nature of decoding, it goes through the decode callback. */
return msc_role_ran_decode(msc_t->c.fi, &msc_t->inter_msc.ho_request,
msc_t_send_stored_ho_request__decode_cb, rtp_ran_local);
}
static void msc_t_fsm_pending_first_co_initial_msg(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct msc_t *msc_t = msc_t_priv(fi);
struct msc_a *msc_a = msub_msc_a(msc_t->c.msub);
struct an_apdu *an_apdu;
OSMO_ASSERT(msc_a);
switch (event) {
case MSC_T_EV_FROM_A_PREPARE_HANDOVER_REQUEST:
/* For an inter-MSC Handover coming in from a remote MSC, we do not yet know the RAN peer and AoIP
* Transport Layer Address.
* - RAN peer is found by decoding the actual Handover Request message and looking for the Cell
* Identifier (Target).
* - To be able to tell the BSC about an AoIP Transport Layer Address, we first need to create an MGW
* endpoint.
* For mere inter-BSC Handover, we know all of the above already. Find out which one this is.
*/
an_apdu = data;
if (!msc_a->c.remote_to) {
/* Inter-BSC */
osmo_fsm_inst_state_chg(msc_t->c.fi, MSC_T_ST_WAIT_HO_REQUEST_ACK, 0, 0);
/* Inter-BSC. All should be set up, just forward the message. */
if (msc_t_down_l2_co(msc_t, an_apdu, true))
msc_t_error("Failed to send AN-APDU to RAN peer\n");
} else {
/* Inter-MSC */
if (msc_t->ran_conn) {
msc_t_error("Unexpected state for inter-MSC Handover: RAN peer is already set up\n");
return;
}
if (msc_t_decode_and_store_ho_request(msc_t, an_apdu))
return;
if (msc_t_find_ran_peer_from_ho_request(msc_t))
return;
/* Relying on timeout of the MGW operations, see onenter() for this state. */
osmo_fsm_inst_state_chg(msc_t->c.fi, MSC_T_ST_WAIT_LOCAL_RTP, 0, 0);
}
return;
case MSC_T_EV_CN_CLOSE:
msc_t_clear(msc_t);
return;
default:
OSMO_ASSERT(false);
}
}
void msc_t_fsm_wait_local_rtp_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct msc_t *msc_t = msc_t_priv(fi);
struct msc_a *msc_a = msub_msc_a(msc_t->c.msub);
/* This only happens on inter-MSC HO incoming from a remote MSC */
if (!msc_a->c.remote_to) {
msc_t_error("Unexpected state: this is not an inter-MSC Handover\n");
return;
}
if (msc_t->inter_msc.call_leg) {
msc_t_error("Unexpected state: call leg already set up\n");
return;
}
msc_t->inter_msc.call_leg = call_leg_alloc(msc_t->c.fi,
MSC_EV_CALL_LEG_TERM,
MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE,
MSC_EV_CALL_LEG_RTP_COMPLETE);
if (!msc_t->inter_msc.call_leg
|| call_leg_ensure_ci(msc_t->inter_msc.call_leg, RTP_TO_RAN, msc_t->inter_msc.callref, NULL, NULL, NULL)
|| call_leg_ensure_ci(msc_t->inter_msc.call_leg, RTP_TO_CN, msc_t->inter_msc.callref, NULL, NULL, NULL)) {
msc_t_error("Failed to set up call leg\n");
return;
}
/* Now wait for two MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, one per RTP connection */
}
void msc_t_fsm_wait_local_rtp(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct msc_t *msc_t = msc_t_priv(fi);
struct rtp_stream *rtps;
switch (event) {
case MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE:
rtps = data;
if (!rtps) {
msc_t_error("Invalid data for MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE\n");
return;
}
/* If both to-RAN and to-CN sides have a CI set up, we can continue. */
if (!call_leg_local_ip(msc_t->inter_msc.call_leg, RTP_TO_RAN)
|| !call_leg_local_ip(msc_t->inter_msc.call_leg, RTP_TO_CN))
return;
osmo_fsm_inst_state_chg(msc_t->c.fi, MSC_T_ST_WAIT_HO_REQUEST_ACK, 0, 0);
msc_t_send_stored_ho_request(msc_t);
return;
case MSC_EV_CALL_LEG_TERM:
msc_t->inter_msc.call_leg = NULL;
msc_t_error("Failed to set up MGW endpoint\n");
return;
case MSC_MNCC_EV_CALL_ENDED:
msc_t->inter_msc.mncc_forwarding_to_remote_cn = NULL;
return;
case MSC_T_EV_CN_CLOSE:
case MSC_T_EV_MO_CLOSE:
msc_t_clear(msc_t);
return;
default:
OSMO_ASSERT(false);
}
}
static int msc_t_patch_and_send_ho_request_ack(struct msc_t *msc_t, const struct an_apdu *incoming_an_apdu,
const struct ran_msg *ran_dec)
{
int rc;
struct rtp_stream *rtp_ran = msc_t->inter_msc.call_leg? msc_t->inter_msc.call_leg->rtp[RTP_TO_RAN] : NULL;
struct rtp_stream *rtp_cn = msc_t->inter_msc.call_leg? msc_t->inter_msc.call_leg->rtp[RTP_TO_CN] : NULL;
/* Since it's BCD, it needs rounded-up half the char* length of an MSISDN plus a type byte.
* But no need to introduce obscure math to save a few stack bytes, just have more. */
uint8_t msisdn_enc_buf[GSM23003_MSISDN_MAX_DIGITS+1];
/* Copy an_apdu and an_apdu->e_info in "copy-on-write" method, because they are const and we
* need to add the Handover Number to e_info. */
const struct ran_handover_request_ack *r = &ran_dec->handover_request_ack;
struct ran_msg ran_enc = *ran_dec;
struct osmo_gsup_message e_info = {};
struct an_apdu an_apdu = {
.an_proto = incoming_an_apdu->an_proto,
.e_info = &e_info,
};
if (incoming_an_apdu->e_info)
e_info = *incoming_an_apdu->e_info;
rc = msc_t_assign_handover_number(msc_t);
if (rc)
return rc;
rc = gsm48_encode_bcd_number(msisdn_enc_buf, sizeof(msisdn_enc_buf), 0,
msc_t->inter_msc.handover_number);
if (rc <= 0)
return -EINVAL;
e_info.msisdn_enc = msisdn_enc_buf;
e_info.msisdn_enc_len = rc;
/* Also need to fetch the RTP IP:port from AoIP Transport Address IE to tell the MGW about it */
if (rtp_ran) {
if (osmo_sockaddr_str_is_nonzero(&r->remote_rtp)) {
LOG_MSC_T(msc_t, LOGL_DEBUG, "From Handover Request Ack, got " OSMO_SOCKADDR_STR_FMT "\n",
OSMO_SOCKADDR_STR_FMT_ARGS(&r->remote_rtp));
rtp_stream_set_remote_addr(rtp_ran, &r->remote_rtp);
} else {
LOG_MSC_T(msc_t, LOGL_DEBUG, "No RTP IP:port in Handover Request Ack\n");
}
if (r->codec_present) {
LOG_MSC_T(msc_t, LOGL_DEBUG, "From Handover Request Ack, got %s\n",
osmo_mgcpc_codec_name(r->codec));
rtp_stream_set_codec(rtp_ran, r->codec);
if (rtp_cn)
rtp_stream_set_codec(rtp_cn, r->codec);
} else {
LOG_MSC_T(msc_t, LOGL_DEBUG, "No codec in Handover Request Ack\n");
}
rtp_stream_commit(rtp_ran);
} else {
LOG_MSC_T(msc_t, LOGL_DEBUG, "No RTP to RAN set up yet\n");
}
/* Remove that AoIP Transport Layer IE so it doesn't get sent to the remote MSC */
ran_enc.handover_request_ack.remote_rtp = (struct osmo_sockaddr_str){};
an_apdu.msg = msc_role_ran_encode(msc_t->c.fi, &ran_enc);
if (!an_apdu.msg)
return -EIO;
/* Send to remote MSC via msc_a_remote role */
rc = msub_role_dispatch(msc_t->c.msub, MSC_ROLE_A, MSC_A_EV_FROM_T_PREPARE_HANDOVER_RESPONSE, &an_apdu);
msgb_free(an_apdu.msg);
return rc;
}
static int msc_t_wait_ho_request_ack_decode_cb(struct osmo_fsm_inst *msc_t_fi, void *data,
const struct ran_msg *ran_dec)
{
int rc;
struct msc_t *msc_t = msc_t_priv(msc_t_fi);
struct msc_a *msc_a = msub_msc_a(msc_t->c.msub);
const struct an_apdu *an_apdu = data;
switch (ran_dec->msg_type) {
case RAN_MSG_HANDOVER_REQUEST_ACK:
if (msc_a->c.remote_to) {
/* inter-MSC. Add Handover Number, remove AoIP Transport Layer Address. */
rc = msc_t_patch_and_send_ho_request_ack(msc_t, an_apdu, ran_dec);
} else {
/* inter-BSC. Just send as-is, with correct event. */
rc = msub_role_dispatch(msc_t->c.msub, MSC_ROLE_A, MSC_A_EV_FROM_T_PREPARE_HANDOVER_RESPONSE,
an_apdu);
}
if (rc)
msc_t_error("Failed to send HO Request Ack\n");
else
osmo_fsm_inst_state_chg(msc_t->c.fi, MSC_T_ST_WAIT_HO_COMPLETE, 0, 0);
return 0;
case RAN_MSG_HANDOVER_FAILURE:
msub_role_dispatch(msc_t->c.msub, MSC_ROLE_A, MSC_A_EV_FROM_T_PREPARE_HANDOVER_FAILURE, an_apdu);
return 0;
case RAN_MSG_CLEAR_REQUEST:
msub_role_dispatch(msc_t->c.msub, MSC_ROLE_A, MSC_A_EV_FROM_T_PROCESS_ACCESS_SIGNALLING_REQUEST,
an_apdu);
return 0;
default:
LOG_MSC_T(msc_t, LOGL_ERROR, "Unexpected message during Prepare Handover procedure: %s\n",
ran_msg_type_name(ran_dec->msg_type));
/* Let's just forward anyway. */
msub_role_dispatch(msc_t->c.msub, MSC_ROLE_A, MSC_A_EV_FROM_T_PROCESS_ACCESS_SIGNALLING_REQUEST,
an_apdu);
return 0;
}
}
static void msc_t_fsm_wait_ho_request_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct msc_t *msc_t = msc_t_priv(fi);
struct an_apdu *an_apdu;
switch (event) {
case MSC_EV_FROM_RAN_UP_L2:
an_apdu = data;
/* For inter-MSC Handover, we need to examine the message type. Depending on the response, we must
* dispatch MSC_A_EV_FROM_T_PREPARE_HANDOVER_RESPONSE or MSC_A_EV_FROM_T_PREPARE_HANDOVER_FAILURE, which
* ensures the correct E-interface message type. And we need to include the Handover Number.
* For mere inter-BSC Handover, we know that our osmo-msc internals don't care much about which event
* dispatches a Handover Failure or Handover Request Ack, so we could skip the decoding. But it is a
* premature optimization that complicates comparing an inter-BSC with an inter-MSC HO. */
msc_role_ran_decode(msc_t->c.fi, an_apdu, msc_t_wait_ho_request_ack_decode_cb, an_apdu);
/* Action continues in msc_t_wait_ho_request_ack_decode_cb() */
return;
case MSC_EV_FROM_RAN_CONN_RELEASED:
msc_t_clear(msc_t);
return;
case MSC_T_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST:
an_apdu = data;
msc_t_down_l2_co(msc_t, an_apdu, false);
return;
case MSC_EV_CALL_LEG_TERM:
msc_t->inter_msc.call_leg = NULL;
msc_t_error("Failed to set up MGW endpoint\n");
return;
case MSC_MNCC_EV_CALL_ENDED:
msc_t->inter_msc.mncc_forwarding_to_remote_cn = NULL;
return;
case MSC_T_EV_CN_CLOSE:
case MSC_T_EV_MO_CLOSE:
msc_t_clear(msc_t);
return;
default:
OSMO_ASSERT(false);
}
}
static int msc_t_wait_ho_complete_decode_cb(struct osmo_fsm_inst *msc_t_fi, void *data,
const struct ran_msg *ran_dec)
{
struct msc_t *msc_t = msc_t_priv(msc_t_fi);
struct msc_a *msc_a = msub_msc_a(msc_t->c.msub);
struct msc_i *msc_i;
const struct an_apdu *an_apdu = data;
switch (ran_dec->msg_type) {
case RAN_MSG_HANDOVER_COMPLETE:
msc_t->ho_success = true;
/* For both inter-BSC local to this MSC and inter-MSC Handover for a remote MSC-A, forward the Handover
* Complete message so that the MSC-A can change the MSC-T (transitional) to a proper MSC-I role. */
msub_role_dispatch(msc_t->c.msub, MSC_ROLE_A, MSC_A_EV_FROM_T_SEND_END_SIGNAL_REQUEST, an_apdu);
/* For inter-BSC Handover, the Handover Complete event has already cleaned up this msc_t, and it is
* already gone and deallocated. */
if (!msc_a->c.remote_to)
return 0;
/* For inter-MSC Handover, the remote MSC-A only turns its msc_t_remote into an msc_i_remote on
* the same GSUP link. We are here on the MSC-B side of the GSUP link and have to take care of
* creating an MSC-I over here to match the msc_i_remote at MSC-A. */
msc_i = msc_i_alloc(msc_t->c.msub, msc_t->c.ran);
if (!msc_i) {
msc_t_error("Failed to create MSC-I role\n");
return -1;
}
msc_i->inter_msc.mncc_forwarding_to_remote_cn = msc_t->inter_msc.mncc_forwarding_to_remote_cn;
mncc_call_reparent(msc_i->inter_msc.mncc_forwarding_to_remote_cn,
msc_i->c.fi, -1, MSC_MNCC_EV_CALL_ENDED, NULL, NULL);
msc_i->inter_msc.call_leg = msc_t->inter_msc.call_leg;
call_leg_reparent(msc_i->inter_msc.call_leg,
msc_i->c.fi,
MSC_EV_CALL_LEG_TERM,
MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE,
MSC_EV_CALL_LEG_RTP_COMPLETE);
/* msc_i_set_ran_conn() properly "steals" the ran_conn from msc_t */
msc_i_set_ran_conn(msc_i, msc_t->ran_conn);
/* Nicked everything worth keeping from MSC-T, discard now. */
msc_t_clear(msc_t);
return 0;
case RAN_MSG_HANDOVER_FAILURE:
msub_role_dispatch(msc_t->c.msub, MSC_ROLE_A, MSC_A_EV_FROM_T_PREPARE_HANDOVER_FAILURE, an_apdu);
return 0;
default:
LOG_MSC_T(msc_t, LOGL_ERROR, "Unexpected message during Prepare Handover procedure: %s\n",
ran_msg_type_name(ran_dec->msg_type));
/* Let's just forward anyway. Fall thru */
case RAN_MSG_HANDOVER_DETECT:
case RAN_MSG_CLEAR_REQUEST:
msub_role_dispatch(msc_t->c.msub, MSC_ROLE_A, MSC_A_EV_FROM_T_PROCESS_ACCESS_SIGNALLING_REQUEST,
an_apdu);
return 0;
}
}
static void msc_t_fsm_wait_ho_complete(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct msc_t *msc_t = msc_t_priv(fi);
struct an_apdu *an_apdu;
switch (event) {
case MSC_EV_FROM_RAN_UP_L2:
an_apdu = data;
/* We need to catch the Handover Complete message in order to send it as a SendEndSignal Request */
msc_role_ran_decode(msc_t->c.fi, an_apdu, msc_t_wait_ho_complete_decode_cb, an_apdu);
return;
case MSC_EV_FROM_RAN_CONN_RELEASED:
msc_t_clear(msc_t);
return;
case MSC_T_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST:
an_apdu = data;
msc_t_down_l2_co(msc_t, an_apdu, false);
return;
case MSC_EV_CALL_LEG_TERM:
msc_t->inter_msc.call_leg = NULL;
msc_t_error("Failed to set up MGW endpoint\n");
return;
case MSC_MNCC_EV_CALL_ENDED:
msc_t->inter_msc.mncc_forwarding_to_remote_cn = NULL;
return;
case MSC_T_EV_CN_CLOSE:
case MSC_T_EV_MO_CLOSE:
msc_t_clear(msc_t);
return;
default:
OSMO_ASSERT(false);
}
}
void msc_t_mncc_cb(struct mncc_call *mncc_call, const union mncc_msg *mncc_msg, void *data)
{
struct msc_t *msc_t = data;
struct gsm_mncc_number nr = {
.plan = 1,
};
OSMO_STRLCPY_ARRAY(nr.number, msc_t->inter_msc.handover_number);
switch (mncc_msg->msg_type) {
case MNCC_RTP_CREATE:
mncc_call_incoming_tx_setup_cnf(mncc_call, &nr);
return;
default:
return;
}
}
struct mncc_call *msc_t_check_call_to_handover_number(const struct gsm_mncc *msg)
{
struct msc_t *msc_t;
const char *handover_number;
struct mncc_call_incoming_req req;
struct mncc_call *mncc_call;
if (!(msg->fields & MNCC_F_CALLED))
return NULL;
handover_number = msg->called.number;
msc_t = msc_t_find_by_handover_number(handover_number);
if (!msc_t)
return NULL;
if (msc_t->inter_msc.mncc_forwarding_to_remote_cn) {
LOG_MSC_T(msc_t, LOGL_ERROR, "Incoming call for inter-MSC call forwarding,"
" but this MSC-T role already has an MNCC FSM set up\n");
return NULL;
}
if (!msc_t->inter_msc.call_leg
|| !msc_t->inter_msc.call_leg->rtp[RTP_TO_CN]) {
LOG_MSC_T(msc_t, LOGL_ERROR, "Incoming call for inter-MSC call forwarding,"
" but this MSC-T has no RTP stream ready for MNCC\n");
return NULL;
}
mncc_call = mncc_call_alloc(msc_t_vsub(msc_t),
msc_t->c.fi,
MSC_MNCC_EV_CALL_COMPLETE,
MSC_MNCC_EV_CALL_ENDED,
msc_t_mncc_cb, msc_t);
if (!mncc_call) {
LOG_MSC_T(msc_t, LOGL_ERROR, "Failed to set up call forwarding from remote MSC\n");
return NULL;
}
msc_t->inter_msc.mncc_forwarding_to_remote_cn = mncc_call;
if (mncc_call_set_rtp_stream(mncc_call, msc_t->inter_msc.call_leg->rtp[RTP_TO_CN])) {
LOG_MSC_T(msc_t, LOGL_ERROR, "Failed to set up call forwarding from remote MSC\n");
osmo_fsm_inst_term(mncc_call->fi, OSMO_FSM_TERM_REGULAR, NULL);
return NULL;
}
req = (struct mncc_call_incoming_req){
.setup_req_msg = *msg,
.bearer_cap_present = true,
.bearer_cap = {
/* TODO derive values from actual config */
/* FIXME are there no defines or enums for these numbers!? */
/* Table 10.5.102/3GPP TS 24.008: Bearer capability information element:
* octet 3 of bearer cap for speech says 3 = "1 1 dual rate support MS/full rate speech version
* 1 preferred, half rate speech version 1 also supported" */
.radio = 3,
/* Table 10.5.103/3GPP TS 24.008 Bearer capability information element:
* 0: FR1, 2: FR2, 4: FR3, 1: HR1, 5: HR3, actually in this order. -1 marks the end of the list. */
.speech_ver = { 0, 2, 4, 1, 5, -1 },
},
};
if (mncc_call_incoming_start(mncc_call, &req)) {
LOG_MSC_T(msc_t, LOGL_ERROR, "Failed to set up call forwarding from remote MSC\n");
osmo_fsm_inst_term(mncc_call->fi, OSMO_FSM_TERM_REGULAR, NULL);
return NULL;
}
return mncc_call;
}
static void msc_t_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct msc_t *msc_t = msc_t_priv(fi);
if (!msc_t->ho_success && !msc_t->ho_fail_sent)
msc_t_send_handover_failure(msc_t, GSM0808_CAUSE_EQUIPMENT_FAILURE);
if (msc_t->ran_conn)
ran_conn_msc_role_gone(msc_t->ran_conn, msc_t->c.fi);
}
#define S(x) (1 << (x))
static const struct osmo_fsm_state msc_t_fsm_states[] = {
[MSC_T_ST_PENDING_FIRST_CO_INITIAL_MSG] = {
.name = "PENDING_FIRST_CO_INITIAL_MSG",
.action = msc_t_fsm_pending_first_co_initial_msg,
.in_event_mask = 0
| S(MSC_T_EV_FROM_A_PREPARE_HANDOVER_REQUEST)
| S(MSC_T_EV_CN_CLOSE)
,
.out_state_mask = 0
| S(MSC_T_ST_WAIT_LOCAL_RTP)
| S(MSC_T_ST_WAIT_HO_REQUEST_ACK)
,
},
[MSC_T_ST_WAIT_LOCAL_RTP] = {
.name = "WAIT_LOCAL_RTP",
.onenter = msc_t_fsm_wait_local_rtp_onenter,
.action = msc_t_fsm_wait_local_rtp,
.in_event_mask = 0
| S(MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE)
| S(MSC_EV_CALL_LEG_TERM)
| S(MSC_MNCC_EV_CALL_ENDED)
| S(MSC_T_EV_CN_CLOSE)
,
.out_state_mask = 0
| S(MSC_T_ST_WAIT_HO_REQUEST_ACK)
,
},
[MSC_T_ST_WAIT_HO_REQUEST_ACK] = {
.name = "WAIT_HO_REQUEST_ACK",
.action = msc_t_fsm_wait_ho_request_ack,
.in_event_mask = 0
| S(MSC_EV_FROM_RAN_UP_L2)
| S(MSC_EV_FROM_RAN_CONN_RELEASED)
| S(MSC_EV_CALL_LEG_TERM)
| S(MSC_MNCC_EV_CALL_ENDED)
| S(MSC_T_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST)
| S(MSC_T_EV_CN_CLOSE)
| S(MSC_T_EV_MO_CLOSE)
,
.out_state_mask = 0
| S(MSC_T_ST_WAIT_HO_COMPLETE)
,
},
[MSC_T_ST_WAIT_HO_COMPLETE] = {
.name = "WAIT_HO_COMPLETE",
.action = msc_t_fsm_wait_ho_complete,
.in_event_mask = 0
| S(MSC_EV_FROM_RAN_UP_L2)
| S(MSC_EV_FROM_RAN_CONN_RELEASED)
| S(MSC_EV_CALL_LEG_TERM)
| S(MSC_MNCC_EV_CALL_ENDED)
| S(MSC_T_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST)
| S(MSC_T_EV_CN_CLOSE)
| S(MSC_T_EV_MO_CLOSE)
,
},
};
const struct value_string msc_t_fsm_event_names[] = {
OSMO_VALUE_STRING(MSC_REMOTE_EV_RX_GSUP),
OSMO_VALUE_STRING(MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE),
OSMO_VALUE_STRING(MSC_EV_CALL_LEG_RTP_COMPLETE),
OSMO_VALUE_STRING(MSC_EV_CALL_LEG_TERM),
OSMO_VALUE_STRING(MSC_MNCC_EV_NEED_LOCAL_RTP),
OSMO_VALUE_STRING(MSC_MNCC_EV_CALL_PROCEEDING),
OSMO_VALUE_STRING(MSC_MNCC_EV_CALL_COMPLETE),
OSMO_VALUE_STRING(MSC_MNCC_EV_CALL_ENDED),
OSMO_VALUE_STRING(MSC_EV_FROM_RAN_COMPLETE_LAYER_3),
OSMO_VALUE_STRING(MSC_EV_FROM_RAN_UP_L2),
OSMO_VALUE_STRING(MSC_EV_FROM_RAN_CONN_RELEASED),
OSMO_VALUE_STRING(MSC_T_EV_FROM_A_PREPARE_HANDOVER_REQUEST),
OSMO_VALUE_STRING(MSC_T_EV_FROM_A_FORWARD_ACCESS_SIGNALLING_REQUEST),
OSMO_VALUE_STRING(MSC_T_EV_CN_CLOSE),
OSMO_VALUE_STRING(MSC_T_EV_MO_CLOSE),
OSMO_VALUE_STRING(MSC_T_EV_CLEAR_COMPLETE),
{}
};
static struct osmo_fsm msc_t_fsm = {
.name = "msc_t",
.states = msc_t_fsm_states,
.num_states = ARRAY_SIZE(msc_t_fsm_states),
.log_subsys = DMSC,
.event_names = msc_t_fsm_event_names,
.cleanup = msc_t_fsm_cleanup,
};
static __attribute__((constructor)) void msc_t_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&msc_t_fsm) == 0);
}
/* Send connection-oriented L3 message to RAN peer (MSC->[BSC|RNC]) */
int msc_t_down_l2_co(struct msc_t *msc_t, const struct an_apdu *an_apdu, bool initial)
{
int rc;
if (!msc_t->ran_conn) {
LOG_MSC_T(msc_t, LOGL_ERROR, "Cannot Tx L2 message: no RAN conn\n");
return -EIO;
}
if (an_apdu->an_proto != msc_t->c.ran->an_proto) {
LOG_MSC_T(msc_t, LOGL_ERROR, "Mismatching AN-APDU proto: %s -- Dropping message\n",
an_proto_name(an_apdu->an_proto));
return -EIO;
}
rc = ran_conn_down_l2_co(msc_t->ran_conn, an_apdu->msg, initial);
if (rc)
LOG_MSC_T(msc_t, LOGL_ERROR, "Failed to transfer message down to new RAN peer (rc=%d)\n", rc);
return rc;
}
struct gsm_network *msc_t_net(const struct msc_t *msc_t)
{
return msub_net(msc_t->c.msub);
}
struct vlr_subscr *msc_t_vsub(const struct msc_t *msc_t)
{
if (!msc_t)
return NULL;
return msub_vsub(msc_t->c.msub);
}
struct msc_t *msc_t_alloc_without_ran_peer(struct msub *msub, struct ran_infra *ran)
{
struct msc_t *msc_t;
msub_role_alloc(msub, MSC_ROLE_T, &msc_t_fsm, struct msc_t, ran);
msc_t = msub_msc_t(msub);
if (!msc_t)
return NULL;
return msc_t;
}
int msc_t_set_ran_peer(struct msc_t *msc_t, struct ran_peer *ran_peer)
{
if (!ran_peer || !ran_peer->sri || !ran_peer->sri->ran) {
LOG_MSC_T(msc_t, LOGL_ERROR, "Invalid RAN peer: %s\n", ran_peer ? ran_peer->fi->id : "NULL");
return -EINVAL;
}
if (ran_peer->sri->ran != msc_t->c.ran) {
LOG_MSC_T(msc_t, LOGL_ERROR, "This MSC-T was set up for %s, cannot assign RAN peer for %s\n",
osmo_rat_type_name(msc_t->c.ran->type), osmo_rat_type_name(ran_peer->sri->ran->type));
return -EINVAL;
}
/* Create a new ran_conn with a fresh conn_id for the outgoing initial message. The msc_t FSM definition ensures
* that the first message sent or received is a Connection-Oriented Initial message. */
msc_t->ran_conn = ran_conn_create_outgoing(ran_peer);
if (!msc_t->ran_conn) {
LOG_MSC_T(msc_t, LOGL_ERROR, "Failed to create outgoing RAN conn\n");
return -EINVAL;
}
msc_t->ran_conn->msc_role = msc_t->c.fi;
msub_update_id(msc_t->c.msub);
return 0;
}
struct msc_t *msc_t_alloc(struct msub *msub, struct ran_peer *ran_peer)
{
struct msc_t *msc_t = msc_t_alloc_without_ran_peer(msub, ran_peer->sri->ran);
if (!msc_t)
return NULL;
if (msc_t_set_ran_peer(msc_t, ran_peer)) {
msc_t_clear(msc_t);
return NULL;
}
return msc_t;
}
void msc_t_clear(struct msc_t *msc_t)
{
if (!msc_t)
return;
osmo_fsm_inst_term(msc_t->c.fi, OSMO_FSM_TERM_REGULAR, msc_t->c.fi);
}