aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/libmsc
diff options
context:
space:
mode:
authorSergey Kostanbaev <sergey.kostanbaev@gmail.com>2015-10-23 20:34:25 +0300
committerIvan Kluchnikov <kluchnikovi@gmail.com>2017-02-07 18:59:53 +0300
commitf5fe345dbb21f78319f60d4bbc45fcb482a7ac92 (patch)
tree55bd60034a8a1094ce82b0be5aa3530a13d3cb45 /openbsc/src/libmsc
parent07a5b120e9d7e306190738b159e84c5be1c0aae0 (diff)
USSD MAP external interface over SUP
Diffstat (limited to 'openbsc/src/libmsc')
-rw-r--r--openbsc/src/libmsc/gsm_04_80.c66
-rw-r--r--openbsc/src/libmsc/gsm_sup.c148
-rw-r--r--openbsc/src/libmsc/ussd.c249
3 files changed, 443 insertions, 20 deletions
diff --git a/openbsc/src/libmsc/gsm_04_80.c b/openbsc/src/libmsc/gsm_04_80.c
index 57c6becab..b785b9d51 100644
--- a/openbsc/src/libmsc/gsm_04_80.c
+++ b/openbsc/src/libmsc/gsm_04_80.c
@@ -63,13 +63,17 @@ static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag,
/* Send response to a mobile-originated ProcessUnstructuredSS-Request */
int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
const struct msgb *in_msg, const char *response_text,
- const struct ussd_request *req)
+ const struct ussd_request *req,
+ uint8_t code,
+ uint8_t ctype,
+ uint8_t mtype)
{
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD RSP");
struct gsm48_hdr *gh;
uint8_t *ptr8;
int response_len;
+#if 1
/* First put the payload text into the message */
ptr8 = msgb_put(msg, 0);
gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), response_text, &response_len);
@@ -80,31 +84,65 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
/* Pre-pend the DCS octet string */
msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F);
+#else
+ response_len = strlen(response_text);
+ if (response_len > MAX_LEN_USSD_STRING)
+ response_len = MAX_LEN_USSD_STRING;
+
+ ptr8 = msgb_put(msg, 0);
+ memcpy(ptr8, response_text, response_len);
+ msgb_put(msg, response_len);
+
+ /* Then wrap it as an Octet String */
+ msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG);
+
+ /* Pre-pend the DCS octet string */
+ msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0xf4);
+#endif
+
/* Then wrap these as a Sequence */
msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
- /* Pre-pend the operation code */
- msgb_push_TLV1(msg, GSM0480_OPERATION_CODE,
- GSM0480_OP_CODE_PROCESS_USS_REQ);
+ if (ctype == GSM0480_CTYPE_RETURN_RESULT) {
+ /* Pre-pend the operation code */
+ msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, code);
- /* Wrap the operation code and IA5 string as a sequence */
- msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
+ /* Wrap the operation code and IA5 string as a sequence */
+ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
- /* Pre-pend the invoke ID */
- msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
+ /* Pre-pend the invoke ID */
+ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
+ } else if (ctype == GSM0480_CTYPE_INVOKE) {
+ /* Pre-pend the operation code */
+ msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, code);
- /* Wrap this up as a Return Result component */
- msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT);
+ /* Pre-pend the invoke ID */
+ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
+ } else {
+ abort();
+ }
- /* Wrap the component in a Facility message */
- msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
+ /* Wrap this up as a Return Result component */
+ msgb_wrap_with_TL(msg, ctype);
+
+ if (mtype == GSM0480_MTYPE_REGISTER ||
+ mtype == GSM0480_MTYPE_RELEASE_COMPLETE) {
+ /* Wrap the component in a Facility message */
+ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
+ } else if (mtype == GSM0480_MTYPE_FACILITY) {
+ uint8_t *data = msgb_push(msg, 1);
+ data[0] = msg->len - 1;
+ } else {
+ abort();
+ }
/* And finally pre-pend the L3 header */
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id
- | (1<<7); /* TI direction = 1 */
- gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+ | (1<<7); /* TI direction = 1 */
+
+ gh->msg_type = mtype;
return gsm0808_submit_dtap(conn, msg, 0, 0);
}
diff --git a/openbsc/src/libmsc/gsm_sup.c b/openbsc/src/libmsc/gsm_sup.c
index ab1766420..b586fa3f3 100644
--- a/openbsc/src/libmsc/gsm_sup.c
+++ b/openbsc/src/libmsc/gsm_sup.c
@@ -28,6 +28,150 @@
#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>
+
+enum {
+ FMAP_MSISDN = 0x80
+};
+
+static int subscr_uss_message(struct msgb *msg,
+ struct ss_request *req,
+ const char* extention)
+{
+ size_t bcd_len = 0;
+ uint8_t *gsup_indicator;
+
+ gsup_indicator = msgb_put(msg, 4);
+
+ /* First byte should always be GPRS_GSUP_MSGT_MAP */
+ gsup_indicator[0] = GPRS_GSUP_MSGT_MAP;
+ gsup_indicator[1] = req->message_type;
+ /* TODO ADD tid */
+ gsup_indicator[2] = 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, 1, &req->ussd_text_language);
+ msgb_tlv_put(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_len, req->ussd_text);
+ }
+
+ if (extention) {
+ uint8_t bcd_buf[32];
+ bcd_len = gsm48_encode_bcd_number(bcd_buf, sizeof(bcd_buf), 0,
+ extention);
+ msgb_tlv_put(msg, FMAP_MSISDN, bcd_len - 1, &bcd_buf[1]);
+ }
+
+ /* fill actual length */
+ gsup_indicator[3] = 3 + 3 + (req->ussd_text_len + 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 subscr_tx_uss_message(struct ss_request *req,
+ struct gsm_subscriber *subscr)
+{
+ struct msgb *msg = gprs_gsup_msgb_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ //GSM0480_OP_CODE_PROCESS_USS_REQ
+ subscr_uss_message(msg, req, subscr->extension);
+
+ return gprs_gsup_client_send(subscr->group->net->sup_client, msg);
+}
+
+
+static int rx_uss_message_parse(struct ss_request *ss,
+ const uint8_t* data,
+ size_t len,
+ char* extention,
+ size_t extention_len)
+{
+ const uint8_t* const_data = data;
+
+ if (len < 1 + 2 + 3 + 3)
+ return -1;
+
+ /* skip GPRS_GSUP_MSGT_MAP */
+ ss->message_type = *(++const_data);
+ ss->component_type = *(++const_data);
+ 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:
+ len = *(++const_data);
+ strncpy((char*)ss->ussd_text,
+ (const char*)++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(DMM, "Unknown code: %d\n", *const_data);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int rx_uss_message(const uint8_t* data, size_t len)
+{
+ char extention[32] = {0};
+ struct ss_request ss;
+ memset(&ss, 0, sizeof(ss));
+
+ if (rx_uss_message_parse(&ss, data, len, extention, sizeof(extention))) {
+ LOGP(DSUP, LOGL_ERROR, "Can't parse uss message\n");
+ return -1;
+ }
+
+ LOGP(DSUP, 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(&ss, extention);
+}
+
static int subscr_tx_sup_message(struct gprs_gsup_client *sup_client,
struct gsm_subscriber *subscr,
@@ -289,6 +433,10 @@ int subscr_rx_sup_message(struct gprs_gsup_client *sup_client, struct msgb *msg)
struct gprs_gsup_message gsup_msg = {0};
struct gsm_subscriber *subscr;
+ if (*data == GPRS_GSUP_MSGT_MAP) {
+ return rx_uss_message(data, data_len);
+ }
+
rc = gprs_gsup_decode(data, data_len, &gsup_msg);
if (rc < 0) {
LOGP(DSUP, LOGL_ERROR,
diff --git a/openbsc/src/libmsc/ussd.c b/openbsc/src/libmsc/ussd.c
index 7f01eae71..c81f777c7 100644
--- a/openbsc/src/libmsc/ussd.c
+++ b/openbsc/src/libmsc/ussd.c
@@ -33,18 +33,233 @@
#include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h>
#include <openbsc/osmo_msc.h>
+#include <openbsc/gsm_sup.h>
+#include <openbsc/ussd.h>
+#include <osmocom/gsm/gsm0480.h>
-/* Declarations of USSD strings to be recognised */
-const char USSD_TEXT_OWN_NUMBER[] = "*#100#";
+struct gsm_ussd {
+ struct llist_head ussqueue;
-/* Forward declarations of network-specific handler functions */
-static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req);
+ uint8_t uniq_id; /**< System wide uniq ID */
+
+ uint8_t invoke_id;
+ uint8_t transaction_id;
+
+ uint8_t current_transaction_id;
+
+ struct gsm_subscriber_connection *conn;
+};
+
+static uint64_t s_uniq_ussd_sessiod_id = 0;
+static LLIST_HEAD(s_active_ussd_sessions);
+
+static struct llist_head *get_active_ussd_sessions(void)
+{
+ return &s_active_ussd_sessions;
+}
+
+
+static struct gsm_ussd* ussd_session_alloc(struct gsm_subscriber_connection* conn)
+{
+ struct gsm_network* net = conn->bts->network;
+ struct gsm_ussd* m = talloc_zero(net, struct gsm_ussd);
+ if (!m)
+ return NULL;
+
+ m->conn = conn;
+ m->uniq_id = s_uniq_ussd_sessiod_id++;
+
+ INIT_LLIST_HEAD(&m->ussqueue);
+ llist_add_tail(&m->ussqueue, &s_active_ussd_sessions);
+
+ DEBUGP(DMM, "Alloc USSD session: %d\n", m->uniq_id);
+ return m;
+}
+
+static void ussd_session_free(struct gsm_ussd* s)
+{
+ DEBUGP(DMM, "Free USSD session: %d\n", s->uniq_id);
+ llist_del(&s->ussqueue);
+ talloc_free(s);
+}
+
+static struct gsm_ussd* get_by_uniq_id(uint8_t invoke_id)
+{
+ struct gsm_ussd* c;
+ llist_for_each_entry(c, get_active_ussd_sessions(), ussqueue) {
+ if (c->uniq_id == invoke_id) {
+ DEBUGP(DMM, "uniq invoke_id %d has %s extention\n",
+ invoke_id, c->conn->subscr->extension);
+ return c;
+ }
+ }
+
+ DEBUGP(DMM, "uniq invoke_id %d hasn't been found\n", invoke_id);
+ return NULL;
+}
+
+static struct gsm_ussd* get_by_id(struct gsm_subscriber_connection *conn, uint8_t invoke_id)
+{
+ struct gsm_ussd* c;
+ llist_for_each_entry(c, get_active_ussd_sessions(), ussqueue) {
+ if (c->conn == conn && c->invoke_id == invoke_id) {
+ DEBUGP(DMM, "invoke_id %d has %s extention\n",
+ invoke_id, c->conn->subscr->extension);
+ return c;
+ }
+ }
+
+ DEBUGP(DMM, "invoke_id %d hasn't been found\n", invoke_id);
+ return NULL;
+}
+
+int on_ussd_response(const struct ss_request *req, const char *extention)
+{
+ struct ussd_request ussd_req;
+ struct gsm_ussd* ussdq = get_by_uniq_id(req->invoke_id);
+ memset(&ussd_req, 0, sizeof(ussd_req));
+ int rc;
+ uint8_t mtype;
+
+ switch (req->opcode) {
+ case GSM0480_OP_CODE_USS_NOTIFY:
+ DEBUGP(DMM, "Network originated USSD Notify is not supported yet!\n");
+
+ if (!ussdq) {
+ mtype = GSM0480_MTYPE_REGISTER;
+ } else {
+ mtype = GSM0480_MTYPE_FACILITY;
+ }
+
+ return -ENOTSUP;
+ case GSM0480_OP_CODE_PROCESS_USS_REQ:
+ if (!ussdq) {
+ DEBUGP(DMM, "Network originated Process USSD Request is not supported yet!\n");
+ // TODO SUP Reject
+ return -ENOTSUP;
+ }
+
+ mtype = GSM0480_MTYPE_RELEASE_COMPLETE;
+ ussd_req.transaction_id = ussdq->transaction_id;
+ break;
+ case GSM0480_OP_CODE_USS_REQUEST:
+ if (!ussdq) {
+ DEBUGP(DMM, "No session was found for invoke_id: %d\n", req->invoke_id);
+ return -EINVAL;
+ }
+
+ mtype = GSM0480_MTYPE_FACILITY;
+ ussd_req.transaction_id = ussdq->current_transaction_id;
+ break;
+ default:
+ // TODO SUP Reject
+ return -EINVAL;
+ }
+
+ ussd_req.invoke_id = ussdq->invoke_id;
+
+ if (req->ussd_text[0]) {
+ rc = gsm0480_send_ussd_response(ussdq->conn,
+ NULL,
+ (const char *)req->ussd_text,
+ &ussd_req,
+ req->opcode,
+ req->component_type,
+ mtype);
+ } else {
+ rc = gsm0480_send_ussd_reject(ussdq->conn, NULL, &ussd_req);
+ }
+ if (rc || mtype == GSM0480_MTYPE_RELEASE_COMPLETE) {
+ ussd_session_free(ussdq);
+ msc_release_connection(ussdq->conn);
+ }
+ return rc;
+}
/* Entrypoint - handler function common to all mobile-originated USSDs */
int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
int rc;
+ struct gsm48_hdr *gh;
+ struct ss_request req;
+ struct gsm_ussd* ussdq = NULL;
+ struct ussd_request ussd_req;
+
+ memset(&req, 0, sizeof(req));
+ memset(&ussd_req, 0, sizeof(ussd_req));
+
+ DEBUGP(DMM, "handle ussd: %s\n", msgb_hexdump(msg));
+
+ gh = msgb_l3(msg);
+ rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req);
+
+ //if (req.ussd_text[0] == 0xFF)
+ // req.ussd_text[0] = '\0';
+
+ if (!rc) {
+ DEBUGP(DMM, "Unhandled SS\n");
+ goto failed;
+ }
+
+ ussdq = get_by_id(conn, req.invoke_id);
+
+ // TODO FIXME !!!! Replace by message_type
+ switch (req.opcode) {
+ case GSM0480_OP_CODE_PROCESS_USS_REQ:
+ if (ussdq) {
+ /* new session with the same id as an open session, destroy both */
+ DEBUGP(DMM, "Duplicate session? invoke_id: %d\n", req.invoke_id);
+ goto failed;
+ }
+
+ if (req.component_type != GSM0480_CTYPE_INVOKE) {
+ DEBUGP(DMM, "processUSS with component_type 0x%02x\n", req.component_type);
+ goto failed;
+ }
+
+ ussdq = ussd_session_alloc(conn);
+ if (!ussdq) {
+ DEBUGP(DMM, "Failed to create new session\n");
+ goto failed;
+ }
+
+ ussdq->conn = conn;
+ ussdq->invoke_id = req.invoke_id;
+ ussdq->transaction_id = req.transaction_id;
+ break;
+
+ case GSM0480_OP_CODE_USS_REQUEST:
+ if (!ussdq) {
+ DEBUGP(DMM, "no session found for USS_REQUEST with invoke_id=%d\n", req.invoke_id);
+ goto failed;
+ }
+ if (req.component_type != GSM0480_CTYPE_RETURN_RESULT) {
+ DEBUGP(DMM, "USS with component_type 0x%02x\n", req.component_type);
+ goto failed;
+ }
+
+ ussdq->current_transaction_id = req.transaction_id;
+ break;
+
+ default:
+ DEBUGP(DMM, "Unhandled opcode: 0x%02x, component_type: 0x%02x, text: %s\n",
+ req.opcode, req.component_type, req.ussd_text);
+ goto failed;
+ }
+
+ // ACHTUNG! FIXME!! FIXME!! Introduce transaction ID instead
+ // Override Invoke ID
+ req.invoke_id = ussdq->uniq_id;
+ rc = subscr_tx_uss_message(&req, conn->subscr);
+ if (rc) {
+ DEBUGP(DMM, "Unable tp send uss over sup reason: %d\n", rc);
+ goto failed;
+ }
+
+ return 0;
+
+#if 0
struct ussd_request req;
struct gsm48_hdr *gh;
@@ -66,15 +281,36 @@ int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg)
DEBUGP(DMM, "USSD: Own number requested\n");
rc = send_own_number(conn, msg, &req);
} else {
- DEBUGP(DMM, "Unhandled USSD %s\n", req.text);
- rc = gsm0480_send_ussd_reject(conn, msg, &req);
+ rc = subscr_tx_uss_message(req, conn->subscr);
+
+
+ //TODO:
}
+#endif
+failed:
+ // TODO handle error on SUP end
+ if (ussdq) {
+ ussd_session_free(ussdq);
+ }
+
+ ussd_req.invoke_id = req.invoke_id;
+ ussd_req.transaction_id = req.transaction_id;
+ gsm0480_send_ussd_reject(conn, msg, &ussd_req);
/* check if we can release it */
msc_release_connection(conn);
return rc;
}
+#if 0
+
+/* Declarations of USSD strings to be recognised */
+const char USSD_TEXT_OWN_NUMBER[] = "*#100#";
+
+/* Forward declarations of network-specific handler functions */
+static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req);
+
+
/* A network-specific handler function */
static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req)
{
@@ -85,3 +321,4 @@ 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, msg, response_string, req);
}
+#endif