aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVadim Yanitskiy <axilirator@gmail.com>2018-06-12 08:21:20 +0700
committerHarald Welte <laforge@gnumonks.org>2018-07-30 15:35:14 +0000
commit8a6ef55ec5838bdac3b5780bfc404c9b732d944f (patch)
tree6c6020d1532aefb25d05cdc9c0a5faaebaf84060
parent8a0e2588e208e1975c74d7e849e66fd205129acb (diff)
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
-rw-r--r--include/osmocom/msc/gsm_09_11.h2
-rw-r--r--src/libmsc/gsm_04_08.c8
-rw-r--r--src/libmsc/gsm_09_11.c245
-rw-r--r--tests/msc_vlr/msc_vlr_test_ss.c10
-rw-r--r--tests/msc_vlr/msc_vlr_test_ss.err19
5 files changed, 223 insertions, 61 deletions
diff --git a/include/osmocom/msc/gsm_09_11.h b/include/osmocom/msc/gsm_09_11.h
index 4e7a5b64b..5e689fb8c 100644
--- a/include/osmocom/msc/gsm_09_11.h
+++ b/include/osmocom/msc/gsm_09_11.h
@@ -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);
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
index c10a701e2..19b0572ee 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -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));
diff --git a/src/libmsc/gsm_09_11.c b/src/libmsc/gsm_09_11.c
index 799dfaa63..219325ed1 100644
--- a/src/libmsc/gsm_09_11.c
+++ b/src/libmsc/gsm_09_11.c
@@ -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 */
- return 0;
}
+ /* 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);
- 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);
+
+ 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;
+ }
+
+ /* 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;
}
+ /* 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;
+
/**
- * 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.
+ * 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.
*/
- trans_free(trans);
+ 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;
- return rc;
+ /* 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;
}
diff --git a/tests/msc_vlr/msc_vlr_test_ss.c b/tests/msc_vlr/msc_vlr_test_ss.c
index f3a1f68e5..f0652b07b 100644
--- a/tests/msc_vlr/msc_vlr_test_ss.c
+++ b/tests/msc_vlr/msc_vlr_test_ss.c
@@ -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);
diff --git a/tests/msc_vlr/msc_vlr_test_ss.err b/tests/msc_vlr/msc_vlr_test_ss.err
index c7f77119e..fce5bb6a6 100644
--- a/tests/msc_vlr/msc_vlr_test_ss.err
+++ b/tests/msc_vlr/msc_vlr_test_ss.err
@@ -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