aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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