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.comFrom: Sergey Kostanbaev <Sergey.Kostanbaev at 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 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 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_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 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> + +/* +* 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); + } +} + -- 1.9.1