libmsc/gsm_09_11.c: forward SS/USSD messages to HLR over GSUP

In order to be able to support external SS/USSD gateway, we should
not terminate the GSM 04.80 messages at OsmoMSC. Instead, we need
to follow the GSM TS 09.11 specification, and forward all messages
unhandled by OsmoMSC to OsmoHLR over GSUP protocol.

This change implements forwarding of MO SS/USSD messages. The
forwarding assumes transcoding between GSM 04.80 messages and
GSUP messages. The payload of Facility IE is carried 'as is'.

As a side-effect, this will disable the osmo-msc internal handler
implementing the "*#100#" for obtaining the subscribers own phone
number.  In order to re-gain this functionality, you will need a
modern osmo-hlr (Change-Id I1d09fab810a6bb9ab02904de72dbc9e8a414f9f9)
and the following line in your osmo-hlr.cfg:
 hlr
  ussd route prefix *#100# internal own-msisdn

TTCN-3 test case: I01de73aced6057328a121577a5a83bc2615fb2d4
Change-Id: Ide5f7e350b537db80cd8326fc59c8bf2e01cb68c
This commit is contained in:
Vadim Yanitskiy 2018-06-12 08:21:20 +07:00 committed by Harald Welte
parent 8a0e2588e2
commit 8a6ef55ec5
5 changed files with 225 additions and 63 deletions

View File

@ -1,5 +1,7 @@
#pragma once
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/gsup.h>
int gsm0911_rcv_nc_ss(struct gsm_subscriber_connection *conn, struct msgb *msg);
int gsm0911_gsup_handler(struct vlr_subscr *vsub, struct osmo_gsup_message *gsup);

View File

@ -1702,7 +1702,13 @@ static int msc_vlr_route_gsup_msg(struct vlr_subscr *vsub,
struct osmo_gsup_message *gsup_msg)
{
switch (gsup_msg->message_type) {
/* Nowhere to route for now */
/* GSM 09.11 code implementing SS/USSD */
case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
case OSMO_GSUP_MSGT_PROC_SS_RESULT:
case OSMO_GSUP_MSGT_PROC_SS_ERROR:
DEBUGP(DMSC, "Routed to GSM 09.11 SS/USSD handler\n");
return gsm0911_gsup_handler(vsub, gsup_msg);
default:
LOGP(DMM, LOGL_ERROR, "No handler found for %s, dropping message...\n",
osmo_gsup_message_type_name(gsup_msg->message_type));

View File

@ -1,6 +1,7 @@
/* (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
*
@ -26,9 +27,12 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/msc/gsm_04_80.h>
#include <osmocom/msc/gsm_subscriber.h>
@ -37,34 +41,21 @@
#include <osmocom/msc/vlr.h>
#include <osmocom/msc/gsm_04_08.h>
#include <osmocom/msc/transaction.h>
#include <osmocom/msc/gsup_client.h>
#include <osmocom/msc/msc_ifaces.h>
/* FIXME: choose a proper range */
static uint32_t new_callref = 0x20000001;
/* Declarations of USSD strings to be recognised */
const char USSD_TEXT_OWN_NUMBER[] = "*#100#";
/* A network-specific handler function */
static int send_own_number(struct gsm_subscriber_connection *conn,
uint8_t tid, uint8_t invoke_id)
{
char *own_number = conn->vsub->msisdn;
char response_string[GSM_EXTENSION_LENGTH + 20];
DEBUGP(DMM, "%s: MSISDN = %s\n", vlr_subscr_name(conn->vsub),
own_number);
/* Need trailing CR as EOT character */
snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number);
return gsm0480_send_ussd_response(conn, tid, invoke_id, response_string);
}
/* Entry point for call independent MO SS messages */
int gsm0911_rcv_nc_ss(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
struct osmo_gsup_message gsup_msg;
struct gsm_trans *trans;
struct ss_request req;
struct msgb *gsup_msgb;
uint16_t facility_ie_len;
uint8_t *facility_ie;
uint8_t pdisc, tid;
uint8_t msg_type;
int rc;
@ -116,47 +107,195 @@ int gsm0911_rcv_nc_ss(struct gsm_subscriber_connection *conn, struct msgb *msg)
cm_service_request_concludes(conn, msg);
}
memset(&req, 0, sizeof(req));
rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req);
if (!rc) {
LOGP(DMM, LOGL_ERROR, "SS/USSD message parsing error, "
"rejecting request...\n");
gsm0480_send_ussd_reject(conn, tid, -1,
GSM_0480_PROBLEM_CODE_TAG_GENERAL,
GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
/* The GSM 04.80 API uses inverted codes (0 means error) */
return -EPROTO;
/* 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) {
LOGP(DMM, LOGL_ERROR, "GSM 04.80 message parsing error, "
"couldn't extract Facility IE\n");
goto error;
}
/* Interrogation or releaseComplete? */
if (req.ussd_text[0] == '\0' || req.ussd_text[0] == 0xFF) {
if (req.ss_code > 0) {
/* Assume interrogateSS or modification of it and reject */
return gsm0480_send_ussd_return_error(conn, tid,
req.invoke_id, GSM0480_ERR_CODE_ILLEGAL_SS_OPERATION);
/* Facility IE is optional for RELEASE COMPLETE */
if (msg_type != GSM0480_MTYPE_RELEASE_COMPLETE) {
if (!facility_ie || facility_ie_len < 2) {
LOGP(DMM, LOGL_ERROR, "GSM 04.80 message parsing error, "
"missing mandatory Facility IE\n");
rc = -EINVAL;
goto error;
}
/* Still assuming a Release-Complete and returning */
}
/* Compose a mew GSUP message */
memset(&gsup_msg, 0x00, sizeof(gsup_msg));
gsup_msg.message_type = OSMO_GSUP_MSGT_PROC_SS_REQUEST;
gsup_msg.session_id = trans->callref;
/**
* 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, conn->vsub->imsi);
/* Allocate GSUP message buffer */
gsup_msgb = gsup_client_msgb_alloc();
if (!gsup_msgb) {
LOGP(DMM, LOGL_ERROR, "Couldn't allocate GSUP message\n");
rc = -ENOMEM;
goto error;
}
/* Encode GSUP message */
rc = osmo_gsup_encode(gsup_msgb, &gsup_msg);
if (rc) {
LOGP(DMM, LOGL_ERROR, "Couldn't encode GSUP message\n");
goto error;
}
/* Finally send */
rc = gsup_client_send(conn->network->vlr->gsup_client, gsup_msgb);
if (rc) {
LOGP(DMM, LOGL_ERROR, "Couldn't send GSUP message\n");
goto error;
}
/* Don't release connection, wait for response */
msc_subscr_conn_communicating(conn);
return 0;
error:
/* Abort transaction on DTAP-interface */
gsm0480_send_ussd_reject(conn, 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;
}
int gsm0911_gsup_handler(struct vlr_subscr *vsub,
struct osmo_gsup_message *gsup_msg)
{
struct vlr_instance *vlr;
struct gsm_network *net;
struct gsm_trans *trans;
struct gsm48_hdr *gh;
struct msgb *ss_msg;
bool trans_end;
/* Associate logging messages with this subscriber */
log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
/* Obtain pointer to vlr_instance */
vlr = vsub->vlr;
OSMO_ASSERT(vlr);
/* Obtain pointer to gsm_network */
net = (struct gsm_network *) vlr->user_ctx;
OSMO_ASSERT(net);
/* Handle errors */
if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
/* FIXME: handle this error somehow! */
return 0;
}
msc_subscr_conn_communicating(conn);
if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.ussd_text)) {
DEBUGP(DMM, "USSD: Own number requested\n");
rc = send_own_number(conn, tid, req.invoke_id);
} else {
DEBUGP(DMM, "Unhandled USSD %s\n", req.ussd_text);
rc = gsm0480_send_ussd_return_error(conn,
tid, req.invoke_id,
GSM0480_ERR_CODE_UNEXPECTED_DATA_VALUE);
/* Attempt to find DTAP-transaction */
trans = trans_find_by_callref(net, gsup_msg->session_id);
if (!trans) {
/* FIXME: network-originated sessions are not supported yet */
LOGP(DMM, LOGL_ERROR, "Network-originated sessions "
"are not supported, dropping request...\n");
return -ENOTSUP;
}
/**
* TODO: as we only handle *#100# for now, and always
* respond with RELEASE COMPLETE, let's manually free
* the transaction here, until the external interface
* is implemented.
*/
trans_free(trans);
/* 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;
return rc;
/**
* 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:
LOGP(DMM, LOGL_ERROR, "Unexpected session state %d\n",
gsup_msg->session_state);
/* FIXME: send ERROR back to the HLR */
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) {
LOGP(DMM, LOGL_ERROR, "Missing mandatory Facility IE "
"for mapped 0x%02x message\n", gh->msg_type);
/* FIXME: send ERROR back to the HLR */
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_tx_dtap(trans->conn, ss_msg);
/* Release transaction if required */
if (trans_end)
trans_free(trans);
return 0;
}

View File

@ -94,11 +94,21 @@ static void _test_ss_ussd(enum ran_type via_ran)
EXPECT_ACCEPTED(true);
/* MT: GSM 04.80 RELEASE COMPLETE with Facility IE */
gsup_expect_tx("20" /* OSMO_GSUP_MSGT_PROC_SS_REQUEST */
"0108" "09710000004026f0" /* IMSI TLV */
"3004" "20000001" /* Session ID TLV */
"3101" "01" /* Session state: BEGIN */
"3515" FACILITY_IE_REQ);
dtap_expect_tx("8b2a" "1c27" FACILITY_IE_RSP);
expect_release_clear(via_ran);
/* MO: GSM 04.80 REGISTER with Facility IE and SS version IE */
ms_sends_msg("0b7b" "1c15" FACILITY_IE_REQ "7f0100");
gsup_rx("20" /* OSMO_GSUP_MSGT_PROC_SS_REQUEST */
"0108" "09710000004026f0" /* IMSI TLV */
"3004" "20000001" /* Session ID TLV */
"3101" "03" /* Session state: END */
"3527" FACILITY_IE_RSP, NULL);
VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d");
ASSERT_RELEASE_CLEAR(via_ran);

View File

@ -162,22 +162,27 @@ DREF VLR subscr MSISDN:46071 usage increases to: 3
DREF MSISDN:46071: MSC conn use + trans_nc_ss == 3 (0x4a: dtap,cm_service,trans_nc_ss)
DMM MSISDN:46071: rx msg GSM0480_MTYPE_REGISTER: received_cm_service_request changes to false
DREF MSISDN:46071: MSC conn use - cm_service == 2 (0x42: dtap,trans_nc_ss)
GSUP --> HLR: OSMO_GSUP_MSGT_PROC_SS_REQUEST: 20010809710000004026f03004200000013101013515a11302010102013b300b04010f0406aa510c061b01
DMM Subscr_Conn(CM_SERVICE_REQ:901700000004620){SUBSCR_CONN_S_ACCEPTED}: Received Event SUBSCR_CONN_E_COMMUNICATING
DMM Subscr_Conn(CM_SERVICE_REQ:901700000004620){SUBSCR_CONN_S_ACCEPTED}: state_chg to SUBSCR_CONN_S_COMMUNICATING
DMM USSD: Own number requested
DMM MSISDN:46071: MSISDN = 46071
DREF MSISDN:46071: MSC conn use - dtap == 1 (0x40: trans_nc_ss)
<-- GSUP rx OSMO_GSUP_MSGT_PROC_SS_REQUEST: 20010809710000004026f03004200000013101033527a225020101302002013b301b04010f0416d9775d0e2ae3e965f73cfd7683d27310cd06bbc51a0d
DVLR GSUP rx 61: 20010809710000004026f03004200000013101033527a225020101302002013b301b04010f0416d9775d0e2ae3e965f73cfd7683d27310cd06bbc51a0d
DREF VLR subscr MSISDN:46071 usage increases to: 4
DMSC Routed to GSM 09.11 SS/USSD handler
DMSC msc_tx 43 bytes to MSISDN:46071 via RAN_GERAN_A
- DTAP --RAN_GERAN_A--> MS: GSM0480_MTYPE_RELEASE_COMPLETE: 8b2a1c27a225020101302002013b301b04010f0416d9775d0e2ae3e965f73cfd7683d27310cd06bbc51a0d
- DTAP matches expected message
DREF VLR subscr MSISDN:46071 usage decreases to: 2
DREF MSISDN:46071: MSC conn use - trans_nc_ss == 1 (0x2: dtap)
DREF MSISDN:46071: MSC conn use - dtap == 0 (0x0: )
DREF VLR subscr MSISDN:46071 usage decreases to: 3
DREF MSISDN:46071: MSC conn use - trans_nc_ss == 0 (0x0: )
DMM Subscr_Conn(CM_SERVICE_REQ:901700000004620){SUBSCR_CONN_S_COMMUNICATING}: Received Event SUBSCR_CONN_E_UNUSED
DMM Subscr_Conn(CM_SERVICE_REQ:901700000004620){SUBSCR_CONN_S_COMMUNICATING}: state_chg to SUBSCR_CONN_S_RELEASING
DREF MSISDN:46071: MSC conn use + release == 1 (0x100: release)
DREF VLR subscr MSISDN:46071 usage increases to: 3
DREF VLR subscr MSISDN:46071 usage decreases to: 2
DREF VLR subscr MSISDN:46071 usage increases to: 4
DREF VLR subscr MSISDN:46071 usage decreases to: 3
- BSSAP Clear --RAN_GERAN_A--> MS
DREF VLR subscr MSISDN:46071 usage decreases to: 2
<-- GSUP rx OSMO_GSUP_MSGT_PROC_SS_REQUEST: vlr_gsupc_read_cb() returns 0
dtap_tx_confirmed == 1
bssap_clear_sent == 1
- all requests serviced, conn has been released