[PATCH 4/8] libmsc: Add external USSD MAP interface

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/OpenBSC@lists.osmocom.org/.

Sergey.Kostanbaev sergey.kostanbaev at gmail.com
Wed Nov 25 11:03:55 UTC 2015


From: Sergey Kostanbaev <Sergey.Kostanbaev at fairwaves.co>

Add simple MAP-like protocol over SUP socket. It forwards USS messages
with originated extension and sequence number.
Add transaction interface for USSD since now we have async interface
for it and USSD session can last for a while in menu scenario.

---
 openbsc/include/openbsc/gsm_ussd_map.h       |  14 +++
 openbsc/include/openbsc/gsm_ussd_map_proto.h |  24 ++++
 openbsc/include/openbsc/transaction.h        |   5 +
 openbsc/include/openbsc/ussd.h               |   9 ++
 openbsc/src/libmsc/Makefile.am               |   3 +-
 openbsc/src/libmsc/gsm_ussd_map.c            |  92 ++++++++++++++
 openbsc/src/libmsc/gsm_ussd_map_proto.c      | 158 ++++++++++++++++++++++++
 openbsc/src/libmsc/transaction.c             |   4 +
 openbsc/src/libmsc/ussd.c                    | 176 +++++++++++++++++++++++++++
 9 files changed, 484 insertions(+), 1 deletion(-)
 create mode 100644 openbsc/include/openbsc/gsm_ussd_map.h
 create mode 100644 openbsc/include/openbsc/gsm_ussd_map_proto.h
 create mode 100644 openbsc/src/libmsc/gsm_ussd_map.c
 create mode 100644 openbsc/src/libmsc/gsm_ussd_map_proto.c

diff --git a/openbsc/include/openbsc/gsm_ussd_map.h b/openbsc/include/openbsc/gsm_ussd_map.h
new file mode 100644
index 0000000..063f421
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_ussd_map.h
@@ -0,0 +1,14 @@
+#ifndef _GSM_USSD_MAP_H
+#define _GSM_USSD_MAP_H
+
+#include <openbsc/gprs_gsup_client.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_ussd_map_proto.h>
+
+int ussd_map_read_cb(struct gprs_gsup_client *sup_client,
+		     struct msgb *msg);
+
+int ussd_map_tx_message(struct gsm_network *net, struct ss_request *req,
+			const char *extension, uint32_t ref);
+
+#endif /* _GSM_USSD_MAP_H */
diff --git a/openbsc/include/openbsc/gsm_ussd_map_proto.h b/openbsc/include/openbsc/gsm_ussd_map_proto.h
new file mode 100644
index 0000000..8190396
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_ussd_map_proto.h
@@ -0,0 +1,24 @@
+#ifndef _GSM_USSD_MAP_PROTO_H
+#define _GSM_USSD_MAP_PROTO_H
+
+#include <osmocom/gsm/gsm0480.h>
+
+
+enum {
+    FMAP_MSISDN        = 0x80
+};
+
+int subscr_uss_message(struct msgb *msg,
+		       struct ss_request *req,
+		       const char* extension,
+		       uint32_t ref);
+
+int rx_uss_message_parse(const uint8_t* data,
+			 size_t len,
+			 struct ss_request *ss,
+			 uint32_t *ref,
+			 char* extention,
+			 size_t extention_len);
+
+
+#endif /* _GSM_USSD_MAP_PROTO_H */
diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h
index 6ef1612..d82e576 100644
--- a/openbsc/include/openbsc/transaction.h
+++ b/openbsc/include/openbsc/transaction.h
@@ -56,6 +56,11 @@ struct gsm_trans {
 
 			struct gsm_sms *sms;
 		} sms;
+		struct {
+			uint8_t invoke_id;
+			uint8_t mo;
+			uint8_t dirty;
+		} ss;
 	};
 };
 
diff --git a/openbsc/include/openbsc/ussd.h b/openbsc/include/openbsc/ussd.h
index 2665468..d72d35f 100644
--- a/openbsc/include/openbsc/ussd.h
+++ b/openbsc/include/openbsc/ussd.h
@@ -5,6 +5,15 @@
 
 #include <osmocom/core/msgb.h>
 
+#define USSD_MO 1
+#define USSD_MT 0
+
 int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg);
 
+
+int on_ussd_response(struct gsm_network *net, uint32_t ref, struct ss_request* req, const char* extention);
+
+
+void _ussd_trans_free(struct gsm_trans *trans);
+
 #endif
diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am
index 18bfa0c..616ed89 100644
--- a/openbsc/src/libmsc/Makefile.am
+++ b/openbsc/src/libmsc/Makefile.am
@@ -19,7 +19,8 @@ libmsc_a_SOURCES =	auth.c \
 			ussd.c \
 			vty_interface_layer3.c \
 			transaction.c \
-			osmo_msc.c ctrl_commands.c meas_feed.c
+			osmo_msc.c ctrl_commands.c meas_feed.c \
+			gsm_ussd_map_proto.c gsm_ussd_map.c
 
 if BUILD_SMPP
 noinst_HEADERS += smpp_smsc.h
diff --git a/openbsc/src/libmsc/gsm_ussd_map.c b/openbsc/src/libmsc/gsm_ussd_map.c
new file mode 100644
index 0000000..21604ca
--- /dev/null
+++ b/openbsc/src/libmsc/gsm_ussd_map.c
@@ -0,0 +1,92 @@
+/* GSM USSD external MAP interface */
+
+/* (C) 2015 by Sergey Kostanbaev <sergey.kostanbaev at 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/>.
+ *
+ */
+
+#include <openbsc/gsm_ussd_map.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/debug.h>
+#include <openbsc/db.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/gsm_04_08_gprs.h>
+#include <openbsc/gprs_gsup_messages.h>
+#include <openbsc/gprs_gsup_client.h>
+#include <openbsc/osmo_msc.h>
+#include <openbsc/gprs_utils.h>
+#include <openbsc/ussd.h>
+
+
+int ussd_map_tx_message(struct gsm_network* net,
+			struct ss_request *req,
+			const char* extension,
+			uint32_t ref)
+{
+	struct msgb *msg = gprs_gsup_msgb_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	subscr_uss_message(msg, req, extension, ref);
+
+	return gprs_gsup_client_send(net->ussd_sup_client, msg);
+}
+
+
+static int ussd_map_rx_message_int(struct gsm_network *net, const uint8_t* data, size_t len)
+{
+	char extension[32] = {0};
+	uint32_t ref;
+	struct ss_request ss;
+	memset(&ss, 0, sizeof(ss));
+
+	if (rx_uss_message_parse(data, len, &ss, &ref, extension, sizeof(extension))) {
+		LOGP(DSS, LOGL_ERROR, "Can't parse SUP MAP SS message\n");
+		return -1;
+	}
+
+	LOGP(DSS, LOGL_ERROR, "Got invoke_id=0x%02x opcode=0x%02x facility=0x%02x text=%s\n",
+	     ss.invoke_id, ss.opcode, ss.component_type, ss.ussd_text);
+
+	return on_ussd_response(net, ref, &ss, extension);
+}
+
+static int ussd_map_rx_message(struct gprs_gsup_client *sup_client, struct msgb *msg)
+{
+	uint8_t *data = msgb_l2(msg);
+	size_t data_len = msgb_l2len(msg);
+	struct gsm_network *gsmnet = (struct gsm_network *)sup_client->data;
+
+	if (*data != GPRS_GSUP_MSGT_USSD_MAP) {
+		return -1;
+	}
+
+	return ussd_map_rx_message_int(gsmnet, data, data_len);
+}
+
+int ussd_map_read_cb(struct gprs_gsup_client *sup_client, struct msgb *msg)
+{
+	int rc;
+
+	rc = ussd_map_rx_message(sup_client, msg);
+	msgb_free(msg);
+	if (rc < 0)
+		return -1;
+
+	return rc;
+}
diff --git a/openbsc/src/libmsc/gsm_ussd_map_proto.c b/openbsc/src/libmsc/gsm_ussd_map_proto.c
new file mode 100644
index 0000000..4fc0829
--- /dev/null
+++ b/openbsc/src/libmsc/gsm_ussd_map_proto.c
@@ -0,0 +1,158 @@
+/* GSM USSD external MAP protocol on pseudo TCAP */
+
+/* (C) 2015 by Sergey Kostanbaev <sergey.kostanbaev at 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/>.
+ *
+ */
+
+#include <openbsc/gsm_ussd_map.h>
+#include <openbsc/gsm_ussd_map_proto.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/debug.h>
+#include <openbsc/db.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/gsm_04_08_gprs.h>
+#include <openbsc/gprs_gsup_messages.h>
+#include <openbsc/gprs_gsup_client.h>
+#include <openbsc/osmo_msc.h>
+#include <openbsc/gprs_utils.h>
+#include <openbsc/ussd.h>
+
+
+int subscr_uss_message(struct msgb *msg,
+		       struct ss_request *req,
+		       const char* extension,
+		       uint32_t ref)
+{
+	size_t bcd_len = 0;
+	uint8_t *gsup_indicator;
+
+	gsup_indicator = msgb_put(msg, 8);
+
+	/* First byte should always be GPRS_GSUP_MSGT_USSD_MAP */
+	gsup_indicator[0] = GPRS_GSUP_MSGT_USSD_MAP;
+	gsup_indicator[1] = req->message_type;
+
+	gsup_indicator[2] = ref >> 24;
+	gsup_indicator[3] = ref >> 16;
+	gsup_indicator[4] = ref >> 7;
+	gsup_indicator[5] = ref;
+
+	gsup_indicator[6] = req->component_type;
+
+	/* invokeId */
+	msgb_tlv_put(msg, GSM0480_COMPIDTAG_INVOKE_ID, 1, &req->invoke_id);
+
+	/* opCode */
+	msgb_tlv_put(msg, GSM0480_OPERATION_CODE, 1, &req->opcode);
+
+	if (req->ussd_text_len > 0) {
+		msgb_tlv_put(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_len + 1, &req->ussd_text_language);
+	}
+
+	if (extension) {
+		uint8_t bcd_buf[32];
+		bcd_len = gsm48_encode_bcd_number(bcd_buf, sizeof(bcd_buf), 0,
+						  extension);
+		msgb_tlv_put(msg, FMAP_MSISDN, bcd_len - 1, &bcd_buf[1]);
+	}
+
+	/* fill actual length */
+	gsup_indicator[7] = 3 + 3 + (req->ussd_text_len + 1 + 2) + (bcd_len + 2);;
+
+	/* wrap with GSM0480_CTYPE_INVOKE */
+	// gsm0480_wrap_invoke(msg, req->opcode, invoke_id);
+	// gsup_indicator = msgb_push(msgb, 1);
+	// gsup_indicator[0] = GPRS_GSUP_MSGT_MAP;
+	return 0;
+}
+
+
+
+int rx_uss_message_parse(const uint8_t* data,
+			 size_t len,
+			 struct ss_request *ss,
+			 uint32_t *pref,
+			 char* extention,
+			 size_t extention_len)
+{
+	const uint8_t* const_data = data;
+	uint32_t ref;
+
+	if (len < 8 + 3 + 3)
+		return -1;
+
+	/* skip GPRS_GSUP_MSGT_MAP */
+	ss->message_type = *(++const_data);
+
+	ref = ((uint32_t)(*(++const_data))) << 24;
+	ref = ((uint32_t)(*(++const_data))) << 16;
+	ref = ((uint32_t)(*(++const_data))) << 8;
+	ref = ((uint32_t)(*(++const_data)));
+	if (pref)
+		*pref = ref;
+
+	ss->component_type = *(++const_data);
+
+	/* skip full len and move to component id */
+	const_data += 2;
+
+	if (*const_data != GSM0480_COMPIDTAG_INVOKE_ID) {
+		return -1;
+	}
+	const_data += 2;
+	ss->invoke_id = *const_data;
+	const_data++;
+
+	//
+	if (*const_data != GSM0480_OPERATION_CODE) {
+		return -1;
+	}
+	const_data += 2;
+	ss->opcode = *const_data;
+	const_data++;
+
+
+	while (const_data - data < len) {
+		uint8_t len;
+		switch (*const_data) {
+		case ASN1_OCTET_STRING_TAG:
+			ss->ussd_text_len = len = (*(++const_data) - 1);
+			ss->ussd_text_language = *(++const_data);
+			memcpy(ss->ussd_text,
+				++const_data,
+				(len > MAX_LEN_USSD_STRING) ? MAX_LEN_USSD_STRING : len);
+			const_data += len;
+			break;
+
+		case FMAP_MSISDN:
+			len = *(++const_data);
+			gsm48_decode_bcd_number(extention,
+						extention_len,
+						const_data,
+						0);
+			const_data += len + 1;
+			break;
+		default:
+			DEBUGP(DSS, "Unknown code: %d\n", *const_data);
+			return -1;
+		}
+	}
+
+	return 0;
+}
diff --git a/openbsc/src/libmsc/transaction.c b/openbsc/src/libmsc/transaction.c
index a750362..89cb2b5 100644
--- a/openbsc/src/libmsc/transaction.c
+++ b/openbsc/src/libmsc/transaction.c
@@ -25,6 +25,7 @@
 #include <osmocom/core/talloc.h>
 #include <openbsc/gsm_subscriber.h>
 #include <openbsc/gsm_04_08.h>
+#include <openbsc/ussd.h>
 #include <openbsc/mncc.h>
 #include <openbsc/paging.h>
 #include <openbsc/osmo_msc.h>
@@ -96,6 +97,9 @@ void trans_free(struct gsm_trans *trans)
 	case GSM48_PDISC_SMS:
 		_gsm411_sms_trans_free(trans);
 		break;
+	case GSM48_PDISC_NC_SS:
+		_ussd_trans_free(trans);
+		break;
 	}
 
 	if (trans->paging_request) {
diff --git a/openbsc/src/libmsc/ussd.c b/openbsc/src/libmsc/ussd.c
index f0426c4..f1e373c 100644
--- a/openbsc/src/libmsc/ussd.c
+++ b/openbsc/src/libmsc/ussd.c
@@ -33,9 +33,21 @@
 #include <openbsc/gsm_subscriber.h>
 #include <openbsc/debug.h>
 #include <openbsc/osmo_msc.h>
+#include <openbsc/gsm_ussd_map.h>
 #include <openbsc/ussd.h>
 #include <osmocom/gsm/gsm_utils.h>
 #include <osmocom/gsm/gsm0480.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <openbsc/transaction.h>
+
+/* Counter of open sessions */
+static unsigned s_ussd_open_sessions = 0;
+
+/* Last uniq generated session id */
+static uint32_t s_uniq_ussd_sessiod_id = 0;
+
+/* Forward declaration of USSD handler for USSD MAP interface */
+static int handle_rcv_ussd_sup(struct gsm_subscriber_connection *conn, struct msgb *msg);
 
 /* Declarations of USSD strings to be recognised */
 const char USSD_TEXT_OWN_NUMBER[] = "*#100#";
@@ -52,6 +64,9 @@ int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg)
 	char request_string[MAX_LEN_USSD_STRING + 1];
 	struct gsm48_hdr *gh;
 
+	if (conn->subscr->group->net->ussd_sup_client)
+		return handle_rcv_ussd_sup(conn, msg);
+
 	memset(&req, 0, sizeof(req));
 	gh = msgb_l3(msg);
 	rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req);
@@ -101,3 +116,164 @@ static int send_own_number(struct gsm_subscriber_connection *conn, const struct
 	snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number);
 	return gsm0480_send_ussd_response(conn, response_string, req);
 }
+
+
+static int ussd_sup_send_reject(struct gsm_network *conn,
+				uint32_t ref, uint8_t invokeid, uint8_t opcode)
+{
+	struct ss_request rej;
+	rej.message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+	rej.component_type = GSM0480_CTYPE_REJECT;
+	rej.invoke_id = invokeid;
+	rej.opcode = opcode;
+	rej.ussd_text_len = 0;
+
+	return ussd_map_tx_message(conn, &rej, NULL, ref);
+}
+
+/* Callback from USSD MAP interface */
+int on_ussd_response(struct gsm_network *net, uint32_t ref, struct ss_request *req, const char *extention)
+{
+	struct gsm_trans *trans = trans_find_by_callref(net, ref);
+	int rc = 0;
+
+	switch (req->message_type) {
+	case GSM0480_MTYPE_REGISTER:
+		DEBUGP(DSS, "Network originated USSD messages isn't supported yet!\n");
+
+		ussd_sup_send_reject(net, ref, req->invoke_id, req->opcode);
+		return 0;
+
+	case GSM0480_MTYPE_FACILITY:
+	case GSM0480_MTYPE_RELEASE_COMPLETE:
+		if (!trans) {
+			DEBUGP(DSS, "No session was found for ref: %d!\n",
+			       ref);
+
+			ussd_sup_send_reject(net, ref, req->invoke_id, req->opcode);
+			return 0;
+		}
+		break;
+	default:
+		DEBUGP(DSS, "Unknown message type 0x%02x\n", req->message_type);
+		ussd_sup_send_reject(net, ref, req->invoke_id, req->opcode);
+		return 0;
+	}
+
+	req->invoke_id = trans->ss.invoke_id;
+	req->transaction_id = (trans->transaction_id << 4) ^ 0x80;
+
+	if (req->component_type != GSM0480_CTYPE_REJECT) {
+		rc = gsm0480_send_ussd(trans->conn, req);
+	} else {
+		rc = gsm0480_send_ussd_reject(trans->conn, req);
+	}
+
+	if (req->message_type == GSM0480_MTYPE_RELEASE_COMPLETE) {
+		struct gsm_subscriber_connection* conn = trans->conn;
+
+		trans_free(trans);
+		msc_release_connection(conn);
+	}
+
+	return rc;
+}
+
+/* Handler function common to all mobile-originated USSDs in case if USSD MAP enabled  */
+static int handle_rcv_ussd_sup(struct gsm_subscriber_connection *conn, struct msgb *msg)
+{
+	int rc = 0;
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	struct ss_request req;
+	struct gsm_trans *trans = NULL;
+	uint8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */
+
+	if (!conn->subscr)
+		return -EIO;
+
+	memset(&req, 0, sizeof(req));
+
+	DEBUGP(DSS, "handle ussd tid=%d: %s\n", transaction_id, msgb_hexdump(msg));
+	trans = trans_find_by_id(conn, GSM48_PDISC_NC_SS, transaction_id);
+
+	rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req);
+	if (!rc) {
+		DEBUGP(DSS, "Unhandled SS\n");
+		if (!trans) {
+			goto transaction_not_found;
+		}
+
+		/* don't know how to process */
+		goto failed_transaction;
+	}
+
+	switch (req.message_type) {
+	case GSM0480_MTYPE_REGISTER:
+		if (trans) {
+			/* we already have a transaction, ignore this message */
+			goto release_conn;
+		}
+
+		trans = trans_alloc(conn->bts->network, conn->subscr,
+				    GSM48_PDISC_NC_SS,
+				    transaction_id, s_uniq_ussd_sessiod_id++);
+		if (!trans) {
+			DEBUGP(DSS, "Failed to create new ussd transaction\n");
+			goto transaction_not_found;
+		}
+
+		trans->conn = conn;
+		trans->ss.invoke_id = req.invoke_id;
+		trans->ss.mo = 1;
+		trans->ss.dirty = 1;
+		break;
+
+	case GSM0480_MTYPE_FACILITY:
+		if (!trans) {
+			DEBUGP(DSS, "no session found invoke_id=%d tid=%d\n",
+			       req.invoke_id, transaction_id);
+			goto transaction_not_found;
+		}
+		break;
+
+	case GSM0480_MTYPE_RELEASE_COMPLETE:
+		if (!trans) {
+			DEBUGP(DSS, "RELEASE_COMPLETE to non-existing transaction!\n");
+			goto release_conn;
+		}
+
+		trans_free(trans);
+		goto release_conn;
+	}
+
+	rc = ussd_map_tx_message(conn->subscr->group->net, &req,
+				 conn->subscr->extension, trans->callref);
+	if (rc) {
+		/* do not send reject if we failed with the message */
+		trans->ss.dirty = 0;
+
+		DEBUGP(DSS, "Unable tp send uss over sup reason: %d\n", rc);
+		goto failed_transaction;
+	}
+	return 0;
+
+failed_transaction:
+	trans_free(trans);
+
+transaction_not_found:
+	gsm0480_send_ussd_reject(conn, &req);
+
+release_conn:
+	msc_release_connection(conn);
+	return rc;
+}
+
+void _ussd_trans_free(struct gsm_trans *trans)
+{
+	if (trans->ss.dirty) {
+		trans->ss.dirty = 0;
+
+		ussd_sup_send_reject(trans->net, trans->callref, trans->ss.invoke_id, 0);
+	}
+}
+
-- 
1.9.1




More information about the OpenBSC mailing list