[4/9] libmsc: Add external USSD MAP interface

Submitted by Sergey Kostanbaev on April 22, 2016, 12:41 p.m.

Details

Message ID 1461328898-8298-4-git-send-email-sergey.kostanbaev@gmail.com
State New
Series "Series without cover letter"
Headers show

Commit Message

Sergey Kostanbaev April 22, 2016, 12:41 p.m.
From: Sergey Kostanbaev <Sergey.Kostanbaev@fairwaves.co>

---
 openbsc/include/openbsc/gsm_ussd_map.h       |  14 ++
 openbsc/include/openbsc/gsm_ussd_map_proto.h |  25 +++
 openbsc/include/openbsc/transaction.h        |   5 +
 openbsc/include/openbsc/ussd.h               |  13 ++
 openbsc/src/libmsc/Makefile.am               |   3 +-
 openbsc/src/libmsc/gsm_ussd_map.c            |  93 +++++++++++
 openbsc/src/libmsc/gsm_ussd_map_proto.c      | 212 ++++++++++++++++++++++++++
 openbsc/src/libmsc/transaction.c             |   4 +
 openbsc/src/libmsc/ussd.c                    | 220 +++++++++++++++++++++++++++
 9 files changed, 588 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

Patch hide | download patch | download mbox

diff --git a/openbsc/include/openbsc/gsm_ussd_map.h b/openbsc/include/openbsc/gsm_ussd_map.h
new file mode 100644
index 0000000..72798b2
--- /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_header *req,
+			const char *extension, uint32_t ref, const uint8_t *component_data);
+
+#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..7faa5b8
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_ussd_map_proto.h
@@ -0,0 +1,25 @@ 
+#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_header *req,
+		       const char* extension,
+		       uint32_t ref,
+		       const uint8_t *component_data);
+
+int rx_uss_message_parse(const uint8_t* data,
+			 size_t len,
+			 struct ss_header *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..b5b073a 100644
--- a/openbsc/include/openbsc/ussd.h
+++ b/openbsc/include/openbsc/ussd.h
@@ -5,6 +5,19 @@ 
 
 #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_header *reqhdr,
+		     const uint8_t *component,
+		     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 5195890..566efe2 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..7ca84b1
--- /dev/null
+++ b/openbsc/src/libmsc/gsm_ussd_map.c
@@ -0,0 +1,93 @@ 
+/* GSM USSD external MAP interface */
+
+/* (C) 2015 by Sergey Kostanbaev <sergey.kostanbaev@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_header *req,
+			const char* extension,
+			uint32_t ref,
+			const uint8_t* component_data)
+{
+	struct msgb *msg = gprs_gsup_msgb_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	subscr_uss_message(msg, req, extension, ref, component_data);
+
+	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_header 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 type=0x%02x len=%d\n",
+	     ss.message_type, ss.component_length);
+
+	return on_ussd_response(net, ref, &ss, data + ss.component_offset, 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..1d48efb
--- /dev/null
+++ b/openbsc/src/libmsc/gsm_ussd_map_proto.c
@@ -0,0 +1,212 @@ 
+/* GSM USSD external MAP protocol on pseudo TCAP */
+
+/* (C) 2015 by Sergey Kostanbaev <sergey.kostanbaev@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>
+
+/*
+* 0 - GPRS_GSUP_MSGT_USSD_MAP constant
+* 1 - LEN
+* 2 - message_type [ REGISTER / FACILITY / RELEASE COMPLETE ]
+* 3,4,5,6 - tid          ID associated with the session
+* 7 - FMAP_MSISDN constant
+* 8 - extention_len
+* 9..x -  extention
+* x+1 .. original MAP message
+*/
+
+int subscr_uss_message(struct msgb *msg,
+		       struct ss_header *req,
+		       const char* extension,
+		       uint32_t ref,
+		       const uint8_t* component_data)
+{
+	uint8_t bcd_lvlen;
+	uint8_t offset = 0;
+	uint8_t *gsup_indicator;
+
+	gsup_indicator = msgb_put(msg, 7);
+
+	/* First byte should always be GPRS_GSUP_MSGT_USSD_MAP */
+	gsup_indicator[offset++] = GPRS_GSUP_MSGT_USSD_MAP;
+	gsup_indicator[offset++] = 0; // Total length
+	gsup_indicator[offset++] = req->message_type;
+
+	gsup_indicator[offset++] = ref >> 24;
+	gsup_indicator[offset++] = ref >> 16;
+	gsup_indicator[offset++] = ref >> 8;
+	gsup_indicator[offset++] = ref;
+
+	if (extension) {
+		gsup_indicator[offset++] = FMAP_MSISDN;
+		bcd_lvlen = gsm48_encode_bcd_number(gsup_indicator + offset,
+						    32, 0, extension);
+
+		offset += bcd_lvlen;
+		msgb_put(msg, bcd_lvlen + 1);
+	}
+
+	if (component_data) {
+		msgb_put(msg, req->component_length);
+		memcpy(gsup_indicator + offset, component_data, req->component_length);
+	}
+
+	gsup_indicator[1] = offset + req->component_length - 2; //except GPRS_GSUP_MSGT_USSD_MAP and length field
+	return 0;
+#if 0
+	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;
+#endif
+}
+
+
+
+int rx_uss_message_parse(const uint8_t* data,
+			 size_t len,
+			 struct ss_header *ss,
+			 uint32_t *pref,
+			 char* extention,
+			 size_t extention_len)
+{
+	uint8_t ext_len;
+	const uint8_t* const_data = data + 1; // Skip constant
+	uint32_t ref;
+	int total_len;
+
+	if (len < 7)
+		return -1;
+
+	/* skip GPRS_GSUP_MSGT_MAP */
+	total_len        = *(const_data++);
+	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;
+
+	total_len -= 4 + 1; // ref + sizeof(len)
+
+	if (*const_data == FMAP_MSISDN) {
+		ext_len = *(++const_data);
+		if (extention) {
+			gsm48_decode_bcd_number(extention,
+						extention_len,
+						const_data,
+						0);
+		}
+		const_data += ext_len + 1;
+		total_len -= ext_len + 2; // tag FMAP_MSISDN + sizeof(len)
+	}
+
+	ss->component_offset = const_data - data;
+	ss->component_length = total_len; //data[ss->component_offset + 1];
+
+	return 0;
+#if 0
+	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;
+#endif
+}
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 3cafe02..4f90149 100644
--- a/openbsc/src/libmsc/ussd.c
+++ b/openbsc/src/libmsc/ussd.c
@@ -33,9 +33,18 @@ 
 #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>
+
+/* 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#";
@@ -55,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));
 	memset(&reqhdr, 0, sizeof(reqhdr));
 	gh = msgb_l3(msg);
@@ -136,3 +148,211 @@  static int send_own_number(struct gsm_subscriber_connection *conn,
 				      gsm0480_compose_ussd_component(&rss),
 				      &rssh);
 }
+
+
+static int ussd_sup_send_reject(struct gsm_network *conn, uint32_t ref)
+{
+	struct ss_header rej;
+	rej.message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+	rej.component_length = 0;
+
+	return ussd_map_tx_message(conn, &rej, NULL, ref, NULL);
+}
+
+/* Callback from USSD MAP interface */
+int on_ussd_response(struct gsm_network *net,
+		     uint32_t ref,
+		     struct ss_header *reqhdr,
+		     const uint8_t* component,
+		     const char *extention)
+{
+	struct gsm_trans *trans = trans_find_by_callref(net, ref);
+	int rc = 0;
+	struct msgb *msg;
+	uint8_t *ptr8;
+	struct ss_header ssrep = *reqhdr;
+
+	switch (reqhdr->message_type) {
+	case GSM0480_MTYPE_REGISTER:
+		DEBUGP(DSS, "Network originated USSD messages isn't supported yet!\n");
+
+		ussd_sup_send_reject(net, ref);
+		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);
+			return 0;
+		}
+		break;
+	default:
+		DEBUGP(DSS, "Unknown message type 0x%02x\n", reqhdr->message_type);
+		ussd_sup_send_reject(net, ref);
+		return 0;
+	}
+
+	msg = gsm48_msgb_alloc();
+	ptr8 = msgb_put(msg, 0);
+
+	memcpy(ptr8, component, reqhdr->component_length);
+	msgb_put(msg, reqhdr->component_length);
+
+	ssrep.transaction_id = (trans->transaction_id << 4) ^ 0x80;
+	rc = gsm0480_send_component(trans->conn, msg, &ssrep);
+
+	if (reqhdr->message_type == GSM0480_MTYPE_RELEASE_COMPLETE) {
+		struct gsm_subscriber_connection* conn = trans->conn;
+
+		trans_free(trans);
+		msc_release_connection(conn);
+	}
+
+	return rc;
+}
+
+static int get_invoke_id(const uint8_t* data, uint8_t len, uint8_t* pinvoke_id)
+{
+	/* 0:    CTYPE tag
+	 * 1..x: CTYPE len
+	 * x:    INVOKE_ID tag
+	 * x+1:  INVOKE_ID len
+	 * x+2:  INVOKE_ID value
+	 */
+	if (len < 5)
+		return 0;
+
+	unsigned inv_offset = 2;
+	switch (data[0]) {
+	case GSM0480_CTYPE_INVOKE:
+	case GSM0480_CTYPE_RETURN_RESULT:
+		if (data[1] > 0x80)
+			inv_offset += data[1] & 0x7f;
+		if (inv_offset + 2 >= len)
+			return 0;
+		if (data[inv_offset] != GSM0480_COMPIDTAG_INVOKE_ID)
+			return 0;
+		*pinvoke_id = data[inv_offset + 2];
+		return 1;
+	}
+	return 0;
+}
+
+/* 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_header reqhdr;
+	struct gsm_trans *trans = NULL;
+	uint8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */
+	uint8_t invoke_id = 0;
+
+	if (!conn->subscr)
+		return -EIO;
+
+	memset(&reqhdr, 0, sizeof(reqhdr));
+
+	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), &reqhdr);
+	if (!rc) {
+		DEBUGP(DSS, "Incorrect SS header\n");
+		if (!trans) {
+			goto release_conn;
+		}
+
+		/* don't know how to process */
+		goto failed_transaction;
+	}
+
+
+	switch (reqhdr.message_type) {
+	case GSM0480_MTYPE_REGISTER:
+		if (trans) {
+			/* we already have a transaction, ignore this message */
+			goto release_conn;
+		}
+		if (!get_invoke_id(gh->data + reqhdr.component_offset,
+				   reqhdr.component_length,
+				   &invoke_id)) {
+			DEBUGP(DSS, "Incorrect InvokeID in transaction\n");
+			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 = invoke_id;
+		trans->ss.mo = 1;
+		trans->ss.dirty = 1;
+		break;
+
+	case GSM0480_MTYPE_FACILITY:
+		if (!trans) {
+			DEBUGP(DSS, "No session found tid=%d\n",
+			       transaction_id);
+
+			if (!get_invoke_id(gh->data + reqhdr.component_offset,
+					   reqhdr.component_length,
+					   &invoke_id)) {
+				DEBUGP(DSS, "Incorrect InvokeID in transaction\n");
+				goto release_conn;
+			}
+
+			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, &reqhdr,
+				 conn->subscr->extension, trans->callref,
+				 gh->data + reqhdr.component_offset);
+	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, invoke_id, transaction_id);
+
+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);
+	}
+}
+