diff options
author | Sergey Kostanbaev <sergey.kostanbaev@gmail.com> | 2015-10-23 20:34:25 +0300 |
---|---|---|
committer | Ivan Kluchnikov <kluchnikovi@gmail.com> | 2017-02-07 18:59:53 +0300 |
commit | f5fe345dbb21f78319f60d4bbc45fcb482a7ac92 (patch) | |
tree | 55bd60034a8a1094ce82b0be5aa3530a13d3cb45 /openbsc/src/libmsc | |
parent | 07a5b120e9d7e306190738b159e84c5be1c0aae0 (diff) |
USSD MAP external interface over SUP
Diffstat (limited to 'openbsc/src/libmsc')
-rw-r--r-- | openbsc/src/libmsc/gsm_04_80.c | 66 | ||||
-rw-r--r-- | openbsc/src/libmsc/gsm_sup.c | 148 | ||||
-rw-r--r-- | openbsc/src/libmsc/ussd.c | 249 |
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 |