586 lines
18 KiB
C
586 lines
18 KiB
C
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
|
|
* (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
* (C) 2009 by Mike Haben <michael.haben@btinternet.com>
|
|
* (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
|
|
*
|
|
* All Rights Reserved
|
|
*
|
|
* 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/>.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* MSC-specific handling of call independent Supplementary
|
|
* Services messages (NC_SS) according to GSM TS 09.11
|
|
* "Signalling interworking for supplementary services".
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
|
|
#include <osmocom/core/linuxlist.h>
|
|
#include <osmocom/core/rate_ctr.h>
|
|
#include <osmocom/core/stat_item.h>
|
|
#include <osmocom/core/utils.h>
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#include <osmocom/gsm/protocol/gsm_04_80.h>
|
|
#include <osmocom/gsm/gsm0480.h>
|
|
#include <osmocom/gsm/tlv.h>
|
|
|
|
#include <osmocom/msc/gsm_04_80.h>
|
|
#include <osmocom/msc/gsm_subscriber.h>
|
|
#include <osmocom/msc/debug.h>
|
|
#include <osmocom/msc/vlr.h>
|
|
#include <osmocom/msc/gsm_04_08.h>
|
|
#include <osmocom/msc/transaction.h>
|
|
#include <osmocom/gsupclient/gsup_client.h>
|
|
#include <osmocom/msc/msc_a.h>
|
|
#include <osmocom/msc/msub.h>
|
|
#include <osmocom/msc/paging.h>
|
|
#include <osmocom/msc/gsup_client_mux.h>
|
|
|
|
/* FIXME: choose a proper range */
|
|
static uint32_t new_callref = 0x20000001;
|
|
|
|
static void ncss_session_timeout_handler(void *_trans)
|
|
{
|
|
struct gsm_trans *trans = (struct gsm_trans *) _trans;
|
|
struct osmo_gsup_message gsup_msg;
|
|
|
|
/* The timeout might be disabled from the VTY */
|
|
if (trans->net->ncss_guard_timeout == 0)
|
|
return;
|
|
|
|
LOG_TRANS(trans, LOGL_NOTICE, "SS/USSD session timeout, releasing\n");
|
|
|
|
/* Indicate connection release to subscriber (if active) */
|
|
if (trans->msc_a != NULL) {
|
|
/* This pair of cause location and value is used by commercial networks */
|
|
msc_send_ussd_release_complete_cause(trans->msc_a, trans->transaction_id,
|
|
GSM48_CAUSE_LOC_PUN_S_LU, GSM48_CC_CAUSE_NORMAL_UNSPEC);
|
|
}
|
|
|
|
/* Terminate GSUP session with EUSE */
|
|
gsup_msg = (struct osmo_gsup_message){
|
|
.message_type = OSMO_GSUP_MSGT_PROC_SS_ERROR,
|
|
|
|
.session_state = OSMO_GSUP_SESSION_STATE_END,
|
|
.session_id = trans->callref,
|
|
.cause = GMM_CAUSE_NET_FAIL,
|
|
|
|
.message_class = OSMO_GSUP_MESSAGE_CLASS_USSD,
|
|
};
|
|
|
|
OSMO_STRLCPY_ARRAY(gsup_msg.imsi, trans->vsub->imsi);
|
|
|
|
gsup_client_mux_tx(trans->net->gcm, &gsup_msg);
|
|
|
|
/* Finally, release this transaction */
|
|
trans_free(trans);
|
|
}
|
|
|
|
/* Entry point for call independent MO SS messages */
|
|
int gsm0911_rcv_nc_ss(struct msc_a *msc_a, struct msgb *msg)
|
|
{
|
|
struct gsm_network *net;
|
|
struct vlr_subscr *vsub;
|
|
struct gsm48_hdr *gh = msgb_l3(msg);
|
|
struct osmo_gsup_message gsup_msg;
|
|
struct gsm_trans *trans;
|
|
uint16_t facility_ie_len;
|
|
uint8_t *facility_ie;
|
|
uint8_t tid;
|
|
uint8_t msg_type;
|
|
int rc;
|
|
|
|
net = msc_a_net(msc_a);
|
|
OSMO_ASSERT(net);
|
|
|
|
vsub = msc_a_vsub(msc_a);
|
|
if (!vsub) {
|
|
LOG_MSC_A(msc_a, LOGL_ERROR, "No vlr_subscr set for this conn\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
msg_type = gsm48_hdr_msg_type(gh);
|
|
tid = gsm48_hdr_trans_id_flip_ti(gh);
|
|
|
|
/* Associate logging messages with this subscriber */
|
|
log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
|
|
|
|
/* Reuse existing transaction, or create a new one */
|
|
trans = trans_find_by_id(msc_a, TRANS_USSD, tid);
|
|
if (!trans) {
|
|
/* Count MS-initiated attempts to establish a NC SS/USSD session */
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(net->msc_ctrs, MSC_CTR_NC_SS_MO_REQUESTS));
|
|
|
|
/**
|
|
* According to GSM TS 04.80, section 2.4.2 "Register
|
|
* (mobile station to network direction)", the REGISTER
|
|
* message is sent by the mobile station to the network
|
|
* to assign a new transaction identifier for call independent
|
|
* supplementary service control and to request or acknowledge
|
|
* a supplementary service.
|
|
*/
|
|
if (msg_type != GSM0480_MTYPE_REGISTER) {
|
|
LOGP(DSS, LOGL_ERROR, "Rx %s message for non-existing transaction (tid-%u)\n",
|
|
gsm48_pdisc_msgtype_name(GSM48_PDISC_NC_SS, msg_type),
|
|
gsm48_hdr_trans_id(gh));
|
|
gsm48_tx_simple(msc_a,
|
|
GSM48_PDISC_NC_SS | (tid << 4),
|
|
GSM0480_MTYPE_RELEASE_COMPLETE);
|
|
return -EINVAL;
|
|
}
|
|
|
|
trans = trans_alloc(net, vsub, TRANS_USSD, tid, new_callref++);
|
|
if (!trans) {
|
|
LOGP(DSS, LOGL_ERROR, " -> No memory for trans\n");
|
|
gsm48_tx_simple(msc_a,
|
|
GSM48_PDISC_NC_SS | (tid << 4),
|
|
GSM0480_MTYPE_RELEASE_COMPLETE);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Init inactivity timer */
|
|
osmo_timer_setup(&trans->ss.timer_guard,
|
|
ncss_session_timeout_handler, trans);
|
|
|
|
/* Count active NC SS/USSD sessions */
|
|
osmo_stat_item_inc(osmo_stat_item_group_get_item(net->statg, MSC_STAT_ACTIVE_NC_SS), 1);
|
|
|
|
trans->dlci = OMSC_LINKID_CB(msg);
|
|
trans->msc_a = msc_a;
|
|
msc_a_get(msc_a, MSC_A_USE_NC_SS);
|
|
|
|
osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_A_EV_TRANSACTION_ACCEPTED, trans);
|
|
|
|
/* An earlier CM Service Request for this SS message now has concluded */
|
|
if (!osmo_use_count_by(&msc_a->use_count, MSC_A_USE_CM_SERVICE_SS))
|
|
LOG_MSC_A(msc_a, LOGL_ERROR,
|
|
"Creating new MO SS transaction without prior CM Service Request\n");
|
|
else
|
|
msc_a_put(msc_a, MSC_A_USE_CM_SERVICE_SS);
|
|
}
|
|
|
|
LOG_TRANS(trans, LOGL_DEBUG, "Received SS/USSD msg %s\n",
|
|
gsm48_pdisc_msgtype_name(GSM48_PDISC_NC_SS, msg_type));
|
|
|
|
/* (Re)schedule the inactivity timer */
|
|
if (net->ncss_guard_timeout > 0) {
|
|
osmo_timer_schedule(&trans->ss.timer_guard, net->ncss_guard_timeout, 0);
|
|
}
|
|
|
|
/* Attempt to extract Facility IE */
|
|
rc = gsm0480_extract_ie_by_tag(gh, msgb_l3len(msg),
|
|
&facility_ie, &facility_ie_len, GSM0480_IE_FACILITY);
|
|
if (rc) {
|
|
LOG_TRANS(trans, LOGL_ERROR, "GSM 04.80 message parsing error, couldn't extract Facility IE\n");
|
|
goto error;
|
|
}
|
|
|
|
/* Facility IE is optional for RELEASE COMPLETE */
|
|
if (msg_type != GSM0480_MTYPE_RELEASE_COMPLETE) {
|
|
if (!facility_ie || facility_ie_len < 2) {
|
|
LOG_TRANS(trans, LOGL_ERROR, "GSM 04.80 message parsing error,"
|
|
" missing mandatory Facility IE\n");
|
|
rc = -EINVAL;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* Compose a mew GSUP message */
|
|
gsup_msg = (struct osmo_gsup_message){
|
|
.message_type = OSMO_GSUP_MSGT_PROC_SS_REQUEST,
|
|
.session_id = trans->callref,
|
|
.message_class = OSMO_GSUP_MESSAGE_CLASS_USSD,
|
|
};
|
|
|
|
/**
|
|
* Perform A-interface to GSUP-interface mapping,
|
|
* according to GSM TS 09.11, table 4.2.
|
|
*/
|
|
switch (msg_type) {
|
|
case GSM0480_MTYPE_REGISTER:
|
|
gsup_msg.session_state = OSMO_GSUP_SESSION_STATE_BEGIN;
|
|
break;
|
|
case GSM0480_MTYPE_FACILITY:
|
|
gsup_msg.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
|
|
break;
|
|
case GSM0480_MTYPE_RELEASE_COMPLETE:
|
|
gsup_msg.session_state = OSMO_GSUP_SESSION_STATE_END;
|
|
break;
|
|
}
|
|
|
|
/* Fill in the (optional) message payload */
|
|
if (facility_ie) {
|
|
gsup_msg.ss_info_len = facility_ie_len;
|
|
gsup_msg.ss_info = facility_ie;
|
|
}
|
|
|
|
/* Fill in subscriber's IMSI */
|
|
OSMO_STRLCPY_ARRAY(gsup_msg.imsi, vsub->imsi);
|
|
|
|
rc = gsup_client_mux_tx(trans->net->gcm, &gsup_msg);
|
|
|
|
/* Should we release connection? Or wait for response? */
|
|
if (msg_type == GSM0480_MTYPE_RELEASE_COMPLETE)
|
|
trans_free(trans);
|
|
|
|
/* Count established MS-initiated NC SS/USSD sessions */
|
|
if (msg_type == GSM0480_MTYPE_REGISTER)
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(net->msc_ctrs, MSC_CTR_NC_SS_MO_ESTABLISHED));
|
|
|
|
return rc;
|
|
|
|
error:
|
|
/* Abort transaction on DTAP-interface */
|
|
msc_send_ussd_reject(msc_a, tid, -1,
|
|
GSM_0480_PROBLEM_CODE_TAG_GENERAL,
|
|
GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
|
|
if (trans)
|
|
trans_free(trans);
|
|
|
|
/* TODO: abort transaction on GSUP interface if any */
|
|
return rc;
|
|
}
|
|
|
|
/* Call-back from paging the B-end of the connection */
|
|
static void ss_paging_cb(struct msc_a *msc_a, struct gsm_trans *trans)
|
|
{
|
|
struct gsm48_hdr *gh;
|
|
struct msgb *ss_msg;
|
|
|
|
if (trans->msc_a) {
|
|
LOG_MSC_A_CAT(msc_a, DPAG, LOGL_ERROR,
|
|
"Handle paging error: transaction already associated with subscriber,"
|
|
" apparently it was already handled. Skip.\n");
|
|
return;
|
|
}
|
|
OSMO_ASSERT(trans->ss.msg);
|
|
|
|
if (msc_a) {
|
|
struct gsm_network *net = msc_a_net(msc_a);
|
|
LOG_MSC_A_CAT(msc_a, DSS, LOGL_DEBUG, "Paging succeeded\n");
|
|
|
|
/* Assign connection */
|
|
msc_a_get(msc_a, MSC_A_USE_NC_SS);
|
|
trans->msc_a = msc_a;
|
|
trans->paging_request = NULL;
|
|
|
|
/* (Re)schedule the inactivity timer */
|
|
if (net->ncss_guard_timeout > 0) {
|
|
osmo_timer_schedule(&trans->ss.timer_guard, net->ncss_guard_timeout, 0);
|
|
}
|
|
|
|
/* Send stored message */
|
|
ss_msg = trans->ss.msg;
|
|
gh = (struct gsm48_hdr *) msgb_push(ss_msg, sizeof(*gh));
|
|
gh->proto_discr = GSM48_PDISC_NC_SS;
|
|
gh->proto_discr |= trans->transaction_id << 4;
|
|
gh->msg_type = GSM0480_MTYPE_REGISTER;
|
|
|
|
/* Sent to the MS, give ownership of ss_msg */
|
|
msc_a_tx_dtap_to_i(msc_a, ss_msg);
|
|
trans->ss.msg = NULL;
|
|
|
|
/* Count established network-initiated NC SS/USSD sessions */
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(net->msc_ctrs, MSC_CTR_NC_SS_MT_ESTABLISHED));
|
|
} else {
|
|
struct osmo_gsup_message gsup_msg;
|
|
|
|
LOG_MSC_A_CAT(msc_a, DSS, LOGL_DEBUG, "Paging expired\n");
|
|
|
|
gsup_msg = (struct osmo_gsup_message){
|
|
.message_class = OSMO_GSUP_MESSAGE_CLASS_USSD,
|
|
.message_type = OSMO_GSUP_MSGT_PROC_SS_ERROR,
|
|
|
|
.session_state = OSMO_GSUP_SESSION_STATE_END,
|
|
.session_id = trans->callref,
|
|
/* FIXME: we need message class specific cause values */
|
|
.cause = GMM_CAUSE_IMPL_DETACHED,
|
|
};
|
|
|
|
/* Fill in subscriber's IMSI */
|
|
OSMO_STRLCPY_ARRAY(gsup_msg.imsi, trans->vsub->imsi);
|
|
|
|
/* Inform HLR/EUSE about the failure */
|
|
gsup_client_mux_tx(trans->net->gcm, &gsup_msg);
|
|
|
|
msgb_free(trans->ss.msg);
|
|
trans->ss.msg = NULL;
|
|
|
|
trans->callref = 0;
|
|
trans->paging_request = NULL;
|
|
trans_free(trans);
|
|
}
|
|
}
|
|
|
|
static struct gsm_trans *establish_nc_ss_trans(struct gsm_network *net,
|
|
struct vlr_subscr *vsub, const struct osmo_gsup_message *gsup_msg)
|
|
{
|
|
struct msc_a *msc_a;
|
|
struct gsm_trans *trans;
|
|
int tid;
|
|
|
|
if (gsup_msg->session_state != OSMO_GSUP_SESSION_STATE_BEGIN) {
|
|
LOGP(DSS, LOGL_ERROR, "Received non-BEGIN message for non-existing transaction\n");
|
|
return NULL;
|
|
}
|
|
|
|
LOGP(DSS, LOGL_DEBUG, "(%s) Establishing a network-originated session (id=0x%x)\n",
|
|
vlr_subscr_name(vsub), gsup_msg->session_id);
|
|
|
|
if (!gsup_msg->ss_info || gsup_msg->ss_info_len < 2) {
|
|
LOGP(DSS, LOGL_ERROR, "Missing mandatory Facility IE\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Obtain an unused transaction ID */
|
|
tid = trans_assign_trans_id(net, vsub, TRANS_USSD);
|
|
if (tid < 0) {
|
|
LOGP(DSS, LOGL_ERROR, "No free transaction ID\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate a new NCSS transaction */
|
|
trans = trans_alloc(net, vsub, TRANS_USSD, tid, gsup_msg->session_id);
|
|
if (!trans) {
|
|
LOGP(DSS, LOGL_ERROR, " -> No memory for trans\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Count active NC SS/USSD sessions */
|
|
osmo_stat_item_inc(osmo_stat_item_group_get_item(net->statg, MSC_STAT_ACTIVE_NC_SS), 1);
|
|
|
|
/* Init inactivity timer */
|
|
osmo_timer_setup(&trans->ss.timer_guard,
|
|
ncss_session_timeout_handler, trans);
|
|
|
|
/* Attempt to find connection */
|
|
msc_a = msc_a_for_vsub(vsub, true);
|
|
if (msc_a) {
|
|
/* Assign connection */
|
|
msc_a_get(msc_a, MSC_A_USE_NC_SS);
|
|
trans->msc_a = msc_a;
|
|
trans->dlci = 0x00; /* SAPI=0, not SACCH */
|
|
return trans;
|
|
}
|
|
|
|
LOG_TRANS(trans, LOGL_DEBUG, "Triggering Paging Request\n");
|
|
|
|
/* Trigger Paging Request */
|
|
trans->paging_request = paging_request_start(vsub, PAGING_CAUSE_SIGNALLING_HIGH_PRIO,
|
|
ss_paging_cb, trans, "GSM 09.11 SS/USSD");
|
|
if (!trans->paging_request) {
|
|
LOG_TRANS(trans, LOGL_ERROR, "Failed to allocate paging token\n");
|
|
trans_free(trans);
|
|
return NULL;
|
|
}
|
|
|
|
/* Store the Facility IE to be sent */
|
|
OSMO_ASSERT(trans->ss.msg == NULL);
|
|
trans->ss.msg = gsm48_msgb_alloc_name("GSM 04.08 SS/USSD");
|
|
msgb_tlv_put(trans->ss.msg, GSM0480_IE_FACILITY,
|
|
gsup_msg->ss_info_len, gsup_msg->ss_info);
|
|
|
|
return trans;
|
|
}
|
|
|
|
/* NC SS specific transaction release.
|
|
* Gets called by trans_free, DO NOT CALL YOURSELF! */
|
|
void _gsm911_nc_ss_trans_free(struct gsm_trans *trans)
|
|
{
|
|
/**
|
|
* TODO: if transaction wasn't properly terminated,
|
|
* we need to do it here by releasing the subscriber
|
|
* connection and sending notification via GSUP...
|
|
*/
|
|
if (trans->ss.msg != NULL)
|
|
msgb_free(trans->ss.msg);
|
|
|
|
/* Stop inactivity timer */
|
|
osmo_timer_del(&trans->ss.timer_guard);
|
|
|
|
/* One session less */
|
|
osmo_stat_item_dec(osmo_stat_item_group_get_item(trans->net->statg, MSC_STAT_ACTIVE_NC_SS),
|
|
1);
|
|
}
|
|
|
|
int gsm0911_gsup_rx(struct gsup_client_mux *gcm, void *data, const struct osmo_gsup_message *gsup_msg)
|
|
{
|
|
struct gsm_network *net = (struct gsm_network *) data;
|
|
struct gsm_trans *trans;
|
|
struct gsm48_hdr *gh;
|
|
struct msgb *ss_msg;
|
|
bool trans_end;
|
|
struct msc_a *msc_a;
|
|
struct vlr_subscr *vsub;
|
|
|
|
vsub = vlr_subscr_find_by_imsi(net->vlr, gsup_msg->imsi, __func__);
|
|
if (!vsub) {
|
|
LOGP(DSS, LOGL_ERROR, "Rx %s for unknown subscriber, rejecting\n",
|
|
osmo_gsup_message_type_name(gsup_msg->message_type));
|
|
gsup_client_mux_tx_error_reply(gcm, gsup_msg, GMM_CAUSE_IMSI_UNKNOWN);
|
|
return -GMM_CAUSE_IMSI_UNKNOWN;
|
|
}
|
|
|
|
/* Associate logging messages with this subscriber */
|
|
log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
|
|
|
|
/* Attempt to find DTAP-transaction */
|
|
trans = trans_find_by_callref(net, gsup_msg->session_id);
|
|
|
|
/* Handle errors */
|
|
if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
|
|
LOGP(DSS, LOGL_NOTICE, "Rx %s from HLR/EUSE (cause=0x%02x, sid=0x%x)\n",
|
|
osmo_gsup_message_type_name(gsup_msg->message_type),
|
|
gsup_msg->cause, gsup_msg->session_id);
|
|
|
|
/* We don't need subscriber info anymore */
|
|
vlr_subscr_put(vsub, __func__);
|
|
|
|
if (!trans) {
|
|
LOGP(DSS, LOGL_ERROR, "No transaction found for "
|
|
"sid=0x%x, nothing to abort\n", gsup_msg->session_id);
|
|
return -ENODEV;
|
|
}
|
|
|
|
LOG_TRANS(trans, LOGL_NOTICE, "Aborting the session: sending RELEASE COMPLETE\n");
|
|
|
|
/* Indicate connection release to subscriber (if active) */
|
|
if (trans->msc_a != NULL) {
|
|
/* TODO: implement GSUP - GSM 04.80 cause mapping */
|
|
msc_send_ussd_release_complete_cause(trans->msc_a, trans->transaction_id,
|
|
GSM48_CAUSE_LOC_PUN_S_LU, GSM48_CC_CAUSE_TEMP_FAILURE);
|
|
}
|
|
|
|
/* Terminate transaction */
|
|
trans_free(trans);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (!trans) {
|
|
/* Count network-initiated attempts to establish a NC SS/USSD session */
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(net->msc_ctrs, MSC_CTR_NC_SS_MT_REQUESTS));
|
|
|
|
/* Attempt to establish a new transaction */
|
|
trans = establish_nc_ss_trans(net, vsub, gsup_msg);
|
|
if (!trans) {
|
|
LOGP(DSS, LOGL_ERROR, "Failed to establish a network-originated "
|
|
"SS/USSD transaction, rejecting %s\n",
|
|
osmo_gsup_message_type_name(gsup_msg->message_type));
|
|
gsup_client_mux_tx_error_reply(gcm, gsup_msg, GMM_CAUSE_NET_FAIL);
|
|
vlr_subscr_put(vsub, __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Wait for Paging Response */
|
|
if (trans->paging_request) {
|
|
vlr_subscr_put(vsub, __func__);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* We don't need subscriber info anymore */
|
|
vlr_subscr_put(vsub, __func__);
|
|
|
|
/* (Re)schedule the inactivity timer */
|
|
if (net->ncss_guard_timeout > 0) {
|
|
osmo_timer_schedule(&trans->ss.timer_guard,
|
|
net->ncss_guard_timeout, 0);
|
|
}
|
|
|
|
/* Allocate and prepare a new MT message */
|
|
ss_msg = gsm48_msgb_alloc_name("GSM 04.08 SS/USSD");
|
|
gh = (struct gsm48_hdr *) msgb_push(ss_msg, sizeof(*gh));
|
|
gh->proto_discr = GSM48_PDISC_NC_SS;
|
|
gh->proto_discr |= trans->transaction_id << 4;
|
|
|
|
/**
|
|
* Perform GSUP-interface to A-interface mapping,
|
|
* according to GSM TS 09.11, table 4.1.
|
|
*
|
|
* TODO: see (note 3), both CONTINUE and END may
|
|
* be also mapped to REGISTER if a new transaction
|
|
* has to be established.
|
|
*/
|
|
switch (gsup_msg->session_state) {
|
|
case OSMO_GSUP_SESSION_STATE_BEGIN:
|
|
gh->msg_type = GSM0480_MTYPE_REGISTER;
|
|
break;
|
|
case OSMO_GSUP_SESSION_STATE_CONTINUE:
|
|
gh->msg_type = GSM0480_MTYPE_FACILITY;
|
|
break;
|
|
case OSMO_GSUP_SESSION_STATE_END:
|
|
gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
|
|
break;
|
|
|
|
/* Missing or incorrect session state */
|
|
case OSMO_GSUP_SESSION_STATE_NONE:
|
|
default:
|
|
LOG_TRANS(trans, LOGL_ERROR, "Unexpected session state %d\n",
|
|
gsup_msg->session_state);
|
|
gsup_client_mux_tx_error_reply(gcm, gsup_msg, GMM_CAUSE_MSGT_INCOMP_P_STATE);
|
|
msgb_free(ss_msg);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Facility IE is optional only for RELEASE COMPLETE */
|
|
if (gh->msg_type != GSM0480_MTYPE_RELEASE_COMPLETE) {
|
|
if (!gsup_msg->ss_info || gsup_msg->ss_info_len < 2) {
|
|
LOG_TRANS(trans, LOGL_ERROR, "Missing mandatory Facility IE "
|
|
"for mapped 0x%02x message\n", gh->msg_type);
|
|
gsup_client_mux_tx_error_reply(gcm, gsup_msg, GMM_CAUSE_INV_MAND_INFO);
|
|
msgb_free(ss_msg);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* Append Facility IE if preset */
|
|
if (gsup_msg->ss_info && gsup_msg->ss_info_len > 2) {
|
|
/* Facility IE carries LV, others carry TLV */
|
|
if (gh->msg_type == GSM0480_MTYPE_FACILITY)
|
|
msgb_lv_put(ss_msg, gsup_msg->ss_info_len, gsup_msg->ss_info);
|
|
else
|
|
msgb_tlv_put(ss_msg, GSM0480_IE_FACILITY,
|
|
gsup_msg->ss_info_len, gsup_msg->ss_info);
|
|
}
|
|
|
|
/* Should we release the transaction? */
|
|
trans_end = (gh->msg_type == GSM0480_MTYPE_RELEASE_COMPLETE);
|
|
|
|
/* Sent to the MS, give ownership of ss_msg */
|
|
msc_a = trans->msc_a;
|
|
if (!msc_a) {
|
|
LOG_TRANS(trans, LOGL_ERROR, "Cannot send SS message, no local MSC-A role defined for subscriber\n");
|
|
gsup_client_mux_tx_error_reply(gcm, gsup_msg, GMM_CAUSE_NET_FAIL);
|
|
msgb_free(ss_msg);
|
|
return -EINVAL;
|
|
}
|
|
msc_a_tx_dtap_to_i(msc_a, ss_msg);
|
|
|
|
/* Release transaction if required */
|
|
if (trans_end)
|
|
trans_free(trans);
|
|
|
|
/* Count established network-initiated NC SS/USSD sessions */
|
|
if (gsup_msg->session_state == OSMO_GSUP_SESSION_STATE_BEGIN)
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(net->msc_ctrs, MSC_CTR_NC_SS_MT_ESTABLISHED));
|
|
|
|
return 0;
|
|
}
|