diff options
author | Sergey.Kostanbaev <Sergey.Kostanbaev@gmail.com> | 2016-02-09 20:21:08 +0300 |
---|---|---|
committer | Ivan Kluchnikov <kluchnikovi@gmail.com> | 2017-02-07 18:59:54 +0300 |
commit | d4839fe14a0d060933f0006d10dc932375a7c7d6 (patch) | |
tree | a640aa5741fa5f75646e6b3e779957639eb8294e /openbsc/src/libmsc | |
parent | db0e216845a7859bf878a891e2a210dbef6395df (diff) |
manual merge SS from sup-ussd-on-master-ss-wip
Diffstat (limited to 'openbsc/src/libmsc')
-rw-r--r-- | openbsc/src/libmsc/Makefile.am | 2 | ||||
-rw-r--r-- | openbsc/src/libmsc/gsm_04_80.c | 167 | ||||
-rw-r--r-- | openbsc/src/libmsc/gsm_sup.c | 7 | ||||
-rw-r--r-- | openbsc/src/libmsc/gsm_ussd_map.c | 93 | ||||
-rw-r--r-- | openbsc/src/libmsc/gsm_ussd_map_proto.c | 212 | ||||
-rw-r--r-- | openbsc/src/libmsc/ussd.c | 517 | ||||
-rw-r--r-- | openbsc/src/libmsc/vty_interface_layer3.c | 10 |
7 files changed, 684 insertions, 324 deletions
diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am index f9bcf2f37..c06b2960d 100644 --- a/openbsc/src/libmsc/Makefile.am +++ b/openbsc/src/libmsc/Makefile.am @@ -47,6 +47,8 @@ libmsc_a_SOURCES = \ osmo_msc.c \ ctrl_commands.c \ meas_feed.c \ + gsm_ussd_map_proto.c \ + gsm_ussd_map.c \ $(NULL) if BUILD_SMPP diff --git a/openbsc/src/libmsc/gsm_04_80.c b/openbsc/src/libmsc/gsm_04_80.c index a8cf3e9bf..716fe751f 100644 --- a/openbsc/src/libmsc/gsm_04_80.c +++ b/openbsc/src/libmsc/gsm_04_80.c @@ -39,7 +39,7 @@ #include <osmocom/core/msgb.h> #include <osmocom/gsm/tlv.h> - +/* This function can handle ASN1 length up to 255 which is enough for USSD */ static inline unsigned char *msgb_wrap_with_ASN1_TL(struct msgb *msgb, uint8_t tag) { uint16_t origlen = msgb->len; @@ -75,56 +75,135 @@ static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, return data; } +static inline unsigned char *msgb_wrap_with_L(struct msgb *msgb) +{ + uint8_t *data = msgb_push(msgb, 1); + + data[0] = msgb->len - 1; + return data; +} -/* Send response to a mobile-originated ProcessUnstructuredSS-Request */ -int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, - int response_text_len, - uint8_t response_lang, - const char *response_text, - const struct ussd_request *req, - uint8_t code, - uint8_t ctype, - uint8_t mtype) +/* Compose universial USSD packet invoke/return_result payload */ +struct msgb *gsm0480_compose_ussd_component(struct ss_request* req) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD RSP"); + uint8_t *ptr8; + + /* First put the payload text into the message */ + ptr8 = msgb_put(msg, 0); + + memcpy(ptr8, req->ussd_text, req->ussd_text_len); + msgb_put(msg, req->ussd_text_len); + + /* Then wrap it as an Octet String */ + msgb_wrap_with_ASN1_TL(msg, ASN1_OCTET_STRING_TAG); + + /* Pre-pend the DCS octet string */ + msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_language); + + /* Then wrap these as a Sequence */ + msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG); + + if (req->component_type == GSM0480_CTYPE_RETURN_RESULT) { + /* Pre-pend the operation code */ + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, req->opcode); + + /* Wrap the operation code and IA5 string as a sequence */ + msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG); + + /* Pre-pend the invoke ID */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); + } else if (req->component_type == GSM0480_CTYPE_INVOKE) { + /* Pre-pend the operation code */ + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, req->opcode); + + /* Pre-pend the invoke ID */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); + } else { + abort(); + } + + /* Wrap this up as an Invoke or a Return Result component */ + msgb_wrap_with_ASN1_TL(msg, req->component_type); + return msg; +} + +#ifndef NO_GSM0480_SEND_FUNC + +int gsm0480_send_component(struct gsm_subscriber_connection *conn, + struct msgb *msg, + struct ss_header* reqhdr) +{ +#if 0 + struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; uint8_t *ptr8; - int response_len; ptr8 = msgb_put(msg, 0); - if (response_text_len < 0) { - /* First put the payload text into the message */ - gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), response_text, &response_len); - msgb_put(msg, response_len); - response_lang = 0x0F; + memcpy(ptr8, component, reqhdr->component_length); + msgb_put(msg, reqhdr->component_length); +#endif + struct gsm48_hdr *gh; + + if (reqhdr->message_type == GSM0480_MTYPE_REGISTER || + reqhdr->message_type == GSM0480_MTYPE_RELEASE_COMPLETE) { + /* Wrap the component in a Facility message, it's not ASN1 */ + msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); + } else if (reqhdr->message_type == GSM0480_MTYPE_FACILITY) { + /* For GSM0480_MTYPE_FACILITY it's LV not TLV */ + msgb_wrap_with_L(msg); } else { - memcpy(ptr8, response_text, response_text_len); - msgb_put(msg, response_text_len); + abort(); } + /* And finally pre-pend the L3 header */ + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS | reqhdr->transaction_id + | (1<<7); /* TI direction = 1 */ + gh->msg_type = reqhdr->message_type; + + DEBUGP(DSS, "Sending SS to mobile: %s\n", msgb_hexdump(msg)); + + return gsm0808_submit_dtap(conn, msg, 0, 0); +} + +#if 0 +/* Compose universial SS packet except Reject opcodes */ +int gsm0480_send_ussd(struct gsm_subscriber_connection *conn, + struct ss_request* req) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + uint8_t *ptr8; + + /* First put the payload text into the message */ + ptr8 = msgb_put(msg, 0); + + memcpy(ptr8, req->ussd_text, req->ussd_text_len); + msgb_put(msg, req->ussd_text_len); + /* Then wrap it as an Octet String */ msgb_wrap_with_ASN1_TL(msg, ASN1_OCTET_STRING_TAG); /* Pre-pend the DCS octet string */ - msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, response_lang); + msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_language); /* Then wrap these as a Sequence */ msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG); - if (ctype == GSM0480_CTYPE_RETURN_RESULT) { + if (req->component_type == GSM0480_CTYPE_RETURN_RESULT) { /* Pre-pend the operation code */ - msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, code); + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, req->opcode); /* Wrap the operation code and IA5 string as a sequence */ msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG); /* Pre-pend the invoke ID */ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); - } else if (ctype == GSM0480_CTYPE_INVOKE) { + } else if (req->component_type == GSM0480_CTYPE_INVOKE) { /* Pre-pend the operation code */ - msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, code); + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, req->opcode); /* Pre-pend the invoke ID */ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); @@ -132,16 +211,16 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, abort(); } - /* Wrap this up as a Return Result component */ - msgb_wrap_with_ASN1_TL(msg, ctype); + /* Wrap this up as an Invoke or a Return Result component */ + msgb_wrap_with_ASN1_TL(msg, req->component_type); - if (mtype == GSM0480_MTYPE_REGISTER || - mtype == GSM0480_MTYPE_RELEASE_COMPLETE) { + if (req->message_type == GSM0480_MTYPE_REGISTER || + req->message_type == GSM0480_MTYPE_RELEASE_COMPLETE) { /* Wrap the component in a Facility message, it's not ASN1 */ 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 if (req->message_type == GSM0480_MTYPE_FACILITY) { + /* For GSM0480_MTYPE_FACILITY it's LV not TLV */ + msgb_wrap_with_L(msg); } else { abort(); } @@ -149,42 +228,48 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, /* 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 */ + | (1<<7); /* TI direction = 1 */ + gh->msg_type = req->message_type; - gh->msg_type = mtype; - - DEBUGP(DSUP, "Sending USSD to mobile: %s\n", msgb_hexdump(msg)); + DEBUGP(DSS, "Sending USSD to mobile: %s\n", msgb_hexdump(msg)); return gsm0808_submit_dtap(conn, msg, 0, 0); } +#endif int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, - const struct ussd_request *req) + uint8_t invoke_id, + uint8_t transaction_id) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD REJ"); - struct gsm48_hdr *gh; + struct ss_header ssh; /* First insert the problem code */ msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, GSM_0480_GEN_PROB_CODE_UNRECOGNISED); /* Before it insert the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); /* Wrap this up as a Reject component */ msgb_wrap_with_ASN1_TL(msg, GSM0480_CTYPE_REJECT); + /* Prepare data for L3 header */ + ssh.transaction_id = transaction_id; + ssh.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; + return gsm0480_send_component(conn, msg, &ssh); +#if 0 /* Wrap the component in a Facility message */ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); /* And finally pre-pend the L3 header */ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_NC_SS; - gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ + gh->proto_discr |= transaction_id | (1<<7); /* TI direction = 1 */ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; return gsm0808_submit_dtap(conn, msg, 0, 0); +#endif } int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, const char *text) @@ -202,3 +287,5 @@ int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn) return -1; return gsm0808_submit_dtap(conn, msg, 0, 0); } + +#endif diff --git a/openbsc/src/libmsc/gsm_sup.c b/openbsc/src/libmsc/gsm_sup.c index f9edec352..9dc8f191e 100644 --- a/openbsc/src/libmsc/gsm_sup.c +++ b/openbsc/src/libmsc/gsm_sup.c @@ -32,6 +32,7 @@ #include <openbsc/gprs_utils.h> #include <openbsc/ussd.h> +#if 0 enum { FMAP_MSISDN = 0x80 }; @@ -171,7 +172,7 @@ static int rx_uss_message(const uint8_t* data, size_t len) return on_ussd_response(&ss, extention); } - +#endif static int subscr_tx_sup_message(struct gprs_gsup_client *sup_client, struct gsm_subscriber *subscr, @@ -438,11 +439,11 @@ static int subscr_rx_sup_message(struct gprs_gsup_client *sup_client, struct msg struct gprs_gsup_message gsup_msg = {0}; struct gsm_subscriber *subscr; - +#if 0 if (*data == GPRS_GSUP_MSGT_MAP) { return rx_uss_message(data, data_len); } - +#endif rc = gprs_gsup_decode(data, data_len, &gsup_msg); if (rc < 0) { LOGP(DSUP, LOGL_ERROR, diff --git a/openbsc/src/libmsc/gsm_ussd_map.c b/openbsc/src/libmsc/gsm_ussd_map.c new file mode 100644 index 000000000..7ca84b133 --- /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 000000000..1d48efbd5 --- /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/ussd.c b/openbsc/src/libmsc/ussd.c index b31f3e1a0..ac032439e 100644 --- a/openbsc/src/libmsc/ussd.c +++ b/openbsc/src/libmsc/ussd.c @@ -33,378 +33,341 @@ #include <openbsc/gsm_subscriber.h> #include <openbsc/debug.h> #include <openbsc/osmo_msc.h> -#include <openbsc/gsm_sup.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> -struct gsm_ussd { - struct llist_head ussqueue; +/* Last uniq generated session id */ +static uint32_t s_uniq_ussd_sessiod_id = 0; - uint8_t uniq_id; /**< System wide uniq ID */ +/* Forward declaration of USSD handler for USSD MAP interface */ +static int handle_rcv_ussd_sup(struct gsm_subscriber_connection *conn, struct msgb *msg); - uint8_t invoke_id; - uint8_t transaction_id; - - uint8_t mobile_originated; +/* Declarations of USSD strings to be recognised */ +const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; - struct gsm_subscriber_connection *conn; -}; +/* Forward declarations of network-specific handler functions */ +static int send_own_number(struct gsm_subscriber_connection *conn, + const struct ss_header *reqhdr, + const struct ss_request *req); -static unsigned s_ussd_open_sessions = 0; -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) +/* Entrypoint - handler function common to all mobile-originated USSDs */ +int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg) { - return &s_active_ussd_sessions; -} + int rc; + struct ss_header reqhdr; + struct ss_request req; + 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); -static struct gsm_ussd* ussd_session_alloc(struct gsm_subscriber_connection* conn, - uint8_t tid, - uint8_t mo) -{ - 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++; - m->transaction_id = tid; - m->mobile_originated = mo; - ++s_ussd_open_sessions; - - INIT_LLIST_HEAD(&m->ussqueue); - llist_add_tail(&m->ussqueue, &s_active_ussd_sessions); - - DEBUGP(DMM, "Alloc USSD session: %d (open: %d)\n", m->uniq_id, s_ussd_open_sessions); - return m; -} + memset(&req, 0, sizeof(req)); + memset(&reqhdr, 0, sizeof(reqhdr)); + gh = msgb_l3(msg); + rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &reqhdr); + if (!rc) { + DEBUGP(DSS, "Incorrect SS header\n"); + msc_release_connection(conn); + return rc; + } -static void ussd_session_free(struct gsm_ussd* s) -{ - --s_ussd_open_sessions; - DEBUGP(DMM, "Free USSD session: %d (open: %d)\n", s->uniq_id, s_ussd_open_sessions); - llist_del(&s->ussqueue); - talloc_free(s); -} + rc = gsm0480_parse_ss_facility(gh->data + reqhdr.component_offset, + reqhdr.component_length, + &req); + if (!rc) { + DEBUGP(DSS, "Unhandled SS\n"); + // TODO req.invoke_id may not be set!!! + rc = gsm0480_send_ussd_reject(conn, req.invoke_id, reqhdr.transaction_id); + msc_release_connection(conn); + return rc; + } -static struct gsm_ussd* get_by_uniq_id(uint8_t uniq_id) -{ - struct gsm_ussd* c; - llist_for_each_entry(c, get_active_ussd_sessions(), ussqueue) { - if (c->uniq_id == uniq_id) { - DEBUGP(DMM, "uniq_id %d has %s extention\n", - uniq_id, c->conn->subscr->extension); - return c; - } + if (reqhdr.message_type == GSM0480_MTYPE_RELEASE_COMPLETE) + return 0; + + if (reqhdr.message_type != GSM0480_MTYPE_REGISTER || + req.component_type != GSM0480_CTYPE_INVOKE || + req.opcode != GSM0480_OP_CODE_PROCESS_USS_REQ || + req.ussd_text_language != 0x0f) + { + DEBUGP(DSS, "Unexpected SS\n"); + rc = gsm0480_send_ussd_reject(conn, req.invoke_id, reqhdr.transaction_id); + msc_release_connection(conn); + return rc; } - DEBUGP(DMM, "uniq_id %d hasn't been found\n", uniq_id); - return NULL; + gsm_7bit_decode_n_ussd(request_string, MAX_LEN_USSD_STRING, req.ussd_text, req.ussd_text_len); + + if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)request_string)) { + DEBUGP(DSS, "USSD: Own number requested\n"); + rc = send_own_number(conn, &reqhdr, &req); + } else { + DEBUGP(DSS, "Unhandled USSD %s\n", request_string); + rc = gsm0480_send_ussd_reject(conn, req.invoke_id, reqhdr.transaction_id); + } + + /* check if we can release it */ + msc_release_connection(conn); + return rc; } -static struct gsm_ussd* get_by_iid(struct gsm_subscriber_connection *conn, uint8_t invoke_id) +/* A network-specific handler function */ +static int send_own_number(struct gsm_subscriber_connection *conn, + const struct ss_header *reqhdr, + const struct ss_request *req) { - 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; - } - } + struct ss_request rss; + struct ss_header rssh; - DEBUGP(DMM, "invoke_id %d hasn't been found\n", invoke_id); - return NULL; + char *own_number = conn->subscr->extension; + char response_string[GSM_EXTENSION_LENGTH + 20]; + int response_len; + + /* Need trailing CR as EOT character */ + snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); + + memset(&rss, 0, sizeof(rss)); + gsm_7bit_encode_n_ussd(rss.ussd_text, MAX_LEN_USSD_STRING, response_string, &response_len); + rss.ussd_text_len = response_len; + rss.ussd_text_language = 0x0f; + + rss.component_type = GSM0480_CTYPE_RETURN_RESULT; + rss.invoke_id = req->invoke_id; + rss.opcode = GSM0480_OP_CODE_PROCESS_USS_REQ; + + rssh.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; + rssh.transaction_id = reqhdr->transaction_id; + + return gsm0480_send_component(conn, + gsm0480_compose_ussd_component(&rss), + &rssh); } -static struct gsm_ussd* get_by_tid(struct gsm_subscriber_connection *conn, uint8_t transaction_id) + +static int ussd_sup_send_reject(struct gsm_network *conn, uint32_t ref) { - struct gsm_ussd* c; - llist_for_each_entry(c, get_active_ussd_sessions(), ussqueue) { - if (c->conn == conn && c->transaction_id == transaction_id) { - DEBUGP(DMM, "transaction_id %d has %s extention\n", - transaction_id, c->conn->subscr->extension); - return c; - } - } + struct ss_header rej; + rej.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; + rej.component_length = 0; - DEBUGP(DMM, "transaction_id %d hasn't been found\n", transaction_id); - return NULL; +#if 0 + rej.component_type = GSM0480_CTYPE_REJECT; + rej.invoke_id = invokeid; + rej.opcode = opcode; + rej.ussd_text_len = 0; +#endif + return ussd_map_tx_message(conn, &rej, NULL, ref, NULL); } -// From SUP -int on_ussd_response(const struct ss_request *req, const char *extention) +/* 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 ussd_request ussd_req; - struct gsm_ussd* ussdq; - memset(&ussd_req, 0, sizeof(ussd_req)); + struct gsm_trans *trans = trans_find_by_callref(net, ref); int rc = 0; + struct msgb *msg; + uint8_t *ptr8; - switch (req->message_type) { + switch (reqhdr->message_type) { case GSM0480_MTYPE_REGISTER: - DEBUGP(DMM, "Network originated USSD messages isn't supported yet!\n"); + DEBUGP(DSS, "Network originated USSD messages isn't supported yet!\n"); - //TODO Send to sup rejection + ussd_sup_send_reject(net, ref); return 0; case GSM0480_MTYPE_FACILITY: case GSM0480_MTYPE_RELEASE_COMPLETE: - // FIXME add uinq_id field - ussdq = get_by_uniq_id(req->invoke_id); - if (!ussdq) { - DEBUGP(DMM, "No session was found for uniq_id: %d!\n", - req->invoke_id); - // TODO SUP Reject + if (!trans) { + DEBUGP(DSS, "No session was found for ref: %d!\n", + ref); + + ussd_sup_send_reject(net, ref); return 0; } break; default: - DEBUGP(DMM, "Unknown message type 0x%02x\n", req->message_type); - // TODO SUP Reject + DEBUGP(DSS, "Unknown message type 0x%02x\n", reqhdr->message_type); + ussd_sup_send_reject(net, ref); return 0; } - ussd_req.transaction_id = ussdq->transaction_id; - ussd_req.invoke_id = ussdq->invoke_id; +#if 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_response(ussdq->conn, - NULL, - (req->ussd_text_language == 0x80) ? -1 : req->ussd_text_len, - req->ussd_text_language, - (const char *)req->ussd_text, - &ussd_req, - req->opcode, - req->component_type, - req->message_type); + rc = gsm0480_send_ussd(trans->conn, req); } else { - rc = gsm0480_send_ussd_reject(ussdq->conn, NULL, &ussd_req); + rc = gsm0480_send_ussd_reject(trans->conn, req); } +#endif + msg = gsm48_msgb_alloc(); + ptr8 = msgb_put(msg, 0); + + memcpy(ptr8, component, reqhdr->component_length); + msgb_put(msg, reqhdr->component_length); + + rc = gsm0480_send_component(trans->conn, msg, reqhdr); - if (req->message_type == GSM0480_MTYPE_RELEASE_COMPLETE) { - msc_release_connection(ussdq->conn); - ussd_session_free(ussdq); + 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 ussd_sup_send_reject(struct gsm_subscriber_connection *conn, - uint8_t uniq_id, uint8_t opcode) +static int get_invoke_id(const uint8_t* data, uint8_t len, uint8_t* pinvoke_id) { - struct ss_request rej; - rej.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; - rej.component_type = GSM0480_CTYPE_REJECT; - rej.invoke_id = uniq_id; - rej.opcode = opcode; - rej.ussd_text_len = 0; + /* 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; - return subscr_tx_uss_message(&rej, conn->subscr); + 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; } -/* Entrypoint - handler function common to all mobile-originated USSDs */ -int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg) +/* 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; - struct ss_request req; - struct gsm_ussd* ussdq = NULL; - struct ussd_request ussd_req; + 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; - memset(&req, 0, sizeof(req)); - memset(&ussd_req, 0, sizeof(ussd_req)); + if (!conn->subscr) + return -EIO; - DEBUGP(DMM, "handle ussd: %s\n", msgb_hexdump(msg)); + memset(&reqhdr, 0, sizeof(reqhdr)); - gh = msgb_l3(msg); - rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &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), &reqhdr); if (!rc) { - DEBUGP(DMM, "Unhandled SS\n"); - ussdq = get_by_tid(conn, req.transaction_id); - if (ussdq) { - ussd_sup_send_reject(conn, ussdq->uniq_id, 0); - goto failed_transaction; + DEBUGP(DSS, "Incorrect SS header\n"); + if (!trans) { + goto release_conn; } - goto transaction_not_found; + /* don't know how to process */ + goto failed_transaction; } - switch (req.message_type) { + + switch (reqhdr.message_type) { case GSM0480_MTYPE_REGISTER: - ussdq = ussd_session_alloc(conn, req.transaction_id, USSD_MO); - if (!ussdq) { - DEBUGP(DMM, "Failed to create new session\n"); + 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; } - ussdq->invoke_id = req.invoke_id; + + trans->conn = conn; + trans->ss.invoke_id = invoke_id; + trans->ss.mo = 1; + trans->ss.dirty = 1; break; + case GSM0480_MTYPE_FACILITY: - ussdq = get_by_tid(conn, req.transaction_id); - if (!ussdq) { - ussdq = get_by_iid(conn, req.invoke_id); - if (!ussdq) { - DEBUGP(DMM, "no session found invoke_id=%d tid=%d\n", - req.invoke_id, req.transaction_id); - goto transaction_not_found; + 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: - // FIXME handle parsing in libosmocore - ussdq = get_by_tid(conn, req.transaction_id); - if (!ussdq) { - DEBUGP(DMM, "RELEASE_COMPLETE to non-existing transaction!\n"); + if (!trans) { + DEBUGP(DSS, "RELEASE_COMPLETE to non-existing transaction!\n"); goto release_conn; } - ussd_session_free(ussdq); - ussd_sup_send_reject(conn, ussdq->uniq_id, req.opcode); + trans_free(trans); goto release_conn; } - req.invoke_id = ussdq->uniq_id; - rc = subscr_tx_uss_message(&req, conn->subscr); + rc = ussd_map_tx_message(conn->subscr->group->net, &reqhdr, + conn->subscr->extension, trans->callref, + gh->data + reqhdr.component_offset); if (rc) { - DEBUGP(DMM, "Unable tp send uss over sup reason: %d\n", 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: - ussd_session_free(ussdq); + trans_free(trans); transaction_not_found: - ussd_req.invoke_id = req.invoke_id; - ussd_req.transaction_id = req.transaction_id; - gsm0480_send_ussd_reject(conn, msg, &ussd_req); + gsm0480_send_ussd_reject(conn, invoke_id, transaction_id); release_conn: msc_release_connection(conn); return rc; - -#if 0 - ussdq = get_by_iid(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; -#endif -#if 0 - struct ussd_request req; - struct gsm48_hdr *gh; - - memset(&req, 0, sizeof(req)); - gh = msgb_l3(msg); - rc = gsm0480_decode_ussd_request(gh, msgb_l3len(msg), &req); - if (!rc) { - DEBUGP(DMM, "Unhandled SS\n"); - rc = gsm0480_send_ussd_reject(conn, msg, &req); - msc_release_connection(conn); - return rc; - } - - /* Release-Complete */ - if (req.text[0] == '\0') - return 0; - - if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.text)) { - DEBUGP(DMM, "USSD: Own number requested\n"); - rc = send_own_number(conn, msg, &req); - } else { - rc = subscr_tx_uss_message(req, conn->subscr); - - - //TODO: - } -#endif -#if 0 -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; -#endif } -#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) +void _ussd_trans_free(struct gsm_trans *trans) { - char *own_number = conn->subscr->extension; - char response_string[GSM_EXTENSION_LENGTH + 20]; + if (trans->ss.dirty) { + trans->ss.dirty = 0; - /* 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, msg, response_string, req); + //ussd_sup_send_reject(trans->net, trans->callref, trans->ss.invoke_id, 0); + ussd_sup_send_reject(trans->net, trans->callref); + } } -#endif + diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c index 3b5778eac..9583a5b29 100644 --- a/openbsc/src/libmsc/vty_interface_layer3.c +++ b/openbsc/src/libmsc/vty_interface_layer3.c @@ -50,7 +50,8 @@ #include <openbsc/sms_queue.h> #include <openbsc/mncc_int.h> #include <openbsc/handover.h> -#include <openbsc/gsm_sup.h> +#include <openbsc/gprs_gsup_client.h> +#include <openbsc/gsm_ussd_map.h> #include <osmocom/vty/logging.h> @@ -1035,19 +1036,20 @@ DEFUN(sup_ussd_destination, sup_ussd_destination_cmd, struct gsm_network *gsmnet = gsmnet_from_vty(vty); if (gsmnet->ussd_sup_client) { - LOGP(DSUP, LOGL_FATAL, "Can't create two USSD SUP clients\n"); + LOGP(DSS, LOGL_FATAL, "Can't create two USSD SUP clients\n"); vty_out(vty, "%%USSD SUP client already configured%s", VTY_NEWLINE); return CMD_WARNING; } gsmnet->ussd_sup_client = gprs_gsup_client_create( - argv[0], atoi(argv[1]), &sup_read_cb); + argv[0], atoi(argv[1]), &ussd_map_read_cb); if (!gsmnet->ussd_sup_client) { - LOGP(DSUP, LOGL_FATAL, "Cannot set up USSD SUP socket\n"); + LOGP(DSS, LOGL_FATAL, "Cannot set up USSD SUP socket\n"); vty_out(vty, "%%Cannot set up USSD SUP socket%s", VTY_NEWLINE); return CMD_WARNING; } + gsmnet->ussd_sup_client->data = gsmnet; return CMD_SUCCESS; } |