diff options
-rw-r--r-- | include/osmocom/msc/gsm_09_11.h | 2 | ||||
-rw-r--r-- | src/libmsc/gsm_04_08.c | 8 | ||||
-rw-r--r-- | src/libmsc/gsm_09_11.c | 245 | ||||
-rw-r--r-- | tests/msc_vlr/msc_vlr_test_ss.c | 10 | ||||
-rw-r--r-- | tests/msc_vlr/msc_vlr_test_ss.err | 19 |
5 files changed, 223 insertions, 61 deletions
diff --git a/include/osmocom/msc/gsm_09_11.h b/include/osmocom/msc/gsm_09_11.h index 4e7a5b64b..5e689fb8c 100644 --- a/include/osmocom/msc/gsm_09_11.h +++ b/include/osmocom/msc/gsm_09_11.h @@ -1,5 +1,7 @@ #pragma once #include <osmocom/core/msgb.h> +#include <osmocom/gsm/gsup.h> int gsm0911_rcv_nc_ss(struct gsm_subscriber_connection *conn, struct msgb *msg); +int gsm0911_gsup_handler(struct vlr_subscr *vsub, struct osmo_gsup_message *gsup); diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c index c10a701e2..19b0572ee 100644 --- a/src/libmsc/gsm_04_08.c +++ b/src/libmsc/gsm_04_08.c @@ -1702,7 +1702,13 @@ static int msc_vlr_route_gsup_msg(struct vlr_subscr *vsub, struct osmo_gsup_message *gsup_msg) { switch (gsup_msg->message_type) { - /* Nowhere to route for now */ + /* GSM 09.11 code implementing SS/USSD */ + case OSMO_GSUP_MSGT_PROC_SS_REQUEST: + case OSMO_GSUP_MSGT_PROC_SS_RESULT: + case OSMO_GSUP_MSGT_PROC_SS_ERROR: + DEBUGP(DMSC, "Routed to GSM 09.11 SS/USSD handler\n"); + return gsm0911_gsup_handler(vsub, gsup_msg); + default: LOGP(DMM, LOGL_ERROR, "No handler found for %s, dropping message...\n", osmo_gsup_message_type_name(gsup_msg->message_type)); diff --git a/src/libmsc/gsm_09_11.c b/src/libmsc/gsm_09_11.c index 799dfaa63..219325ed1 100644 --- a/src/libmsc/gsm_09_11.c +++ b/src/libmsc/gsm_09_11.c @@ -1,6 +1,7 @@ /* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org> * (C) 2009 by Mike Haben <michael.haben@btinternet.com> + * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com> * * All Rights Reserved * @@ -26,9 +27,12 @@ */ #include <stdio.h> -#include <stdlib.h> -#include <string.h> #include <errno.h> +#include <stdbool.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/tlv.h> #include <osmocom/msc/gsm_04_80.h> #include <osmocom/msc/gsm_subscriber.h> @@ -37,34 +41,21 @@ #include <osmocom/msc/vlr.h> #include <osmocom/msc/gsm_04_08.h> #include <osmocom/msc/transaction.h> +#include <osmocom/msc/gsup_client.h> +#include <osmocom/msc/msc_ifaces.h> /* FIXME: choose a proper range */ static uint32_t new_callref = 0x20000001; -/* Declarations of USSD strings to be recognised */ -const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; - -/* A network-specific handler function */ -static int send_own_number(struct gsm_subscriber_connection *conn, - uint8_t tid, uint8_t invoke_id) -{ - char *own_number = conn->vsub->msisdn; - char response_string[GSM_EXTENSION_LENGTH + 20]; - - DEBUGP(DMM, "%s: MSISDN = %s\n", vlr_subscr_name(conn->vsub), - own_number); - - /* 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, tid, invoke_id, response_string); -} - /* Entry point for call independent MO SS messages */ int gsm0911_rcv_nc_ss(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); + struct osmo_gsup_message gsup_msg; struct gsm_trans *trans; - struct ss_request req; + struct msgb *gsup_msgb; + uint16_t facility_ie_len; + uint8_t *facility_ie; uint8_t pdisc, tid; uint8_t msg_type; int rc; @@ -116,47 +107,195 @@ int gsm0911_rcv_nc_ss(struct gsm_subscriber_connection *conn, struct msgb *msg) cm_service_request_concludes(conn, msg); } - memset(&req, 0, sizeof(req)); - rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req); - if (!rc) { - LOGP(DMM, LOGL_ERROR, "SS/USSD message parsing error, " - "rejecting request...\n"); - gsm0480_send_ussd_reject(conn, tid, -1, - GSM_0480_PROBLEM_CODE_TAG_GENERAL, - GSM_0480_GEN_PROB_CODE_UNRECOGNISED); - /* The GSM 04.80 API uses inverted codes (0 means error) */ - return -EPROTO; + /* Attempt to extract Facility IE */ + rc = gsm0480_extract_ie_by_tag(gh, msgb_l3len(msg), + &facility_ie, &facility_ie_len, GSM0480_IE_FACILITY); + if (rc) { + LOGP(DMM, LOGL_ERROR, "GSM 04.80 message parsing error, " + "couldn't extract Facility IE\n"); + goto error; } - /* Interrogation or releaseComplete? */ - if (req.ussd_text[0] == '\0' || req.ussd_text[0] == 0xFF) { - if (req.ss_code > 0) { - /* Assume interrogateSS or modification of it and reject */ - return gsm0480_send_ussd_return_error(conn, tid, - req.invoke_id, GSM0480_ERR_CODE_ILLEGAL_SS_OPERATION); + /* Facility IE is optional for RELEASE COMPLETE */ + if (msg_type != GSM0480_MTYPE_RELEASE_COMPLETE) { + if (!facility_ie || facility_ie_len < 2) { + LOGP(DMM, LOGL_ERROR, "GSM 04.80 message parsing error, " + "missing mandatory Facility IE\n"); + rc = -EINVAL; + goto error; } - /* Still assuming a Release-Complete and returning */ - return 0; } + /* Compose a mew GSUP message */ + memset(&gsup_msg, 0x00, sizeof(gsup_msg)); + gsup_msg.message_type = OSMO_GSUP_MSGT_PROC_SS_REQUEST; + gsup_msg.session_id = trans->callref; + + /** + * Perform A-interface to GSUP-interface mapping, + * according to GSM TS 09.11, table 4.2. + */ + switch (msg_type) { + case GSM0480_MTYPE_REGISTER: + gsup_msg.session_state = OSMO_GSUP_SESSION_STATE_BEGIN; + break; + case GSM0480_MTYPE_FACILITY: + gsup_msg.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE; + break; + case GSM0480_MTYPE_RELEASE_COMPLETE: + gsup_msg.session_state = OSMO_GSUP_SESSION_STATE_END; + break; + } + + /* Fill in the (optional) message payload */ + if (facility_ie) { + gsup_msg.ss_info_len = facility_ie_len; + gsup_msg.ss_info = facility_ie; + } + + /* Fill in subscriber's IMSI */ + OSMO_STRLCPY_ARRAY(gsup_msg.imsi, conn->vsub->imsi); + + /* Allocate GSUP message buffer */ + gsup_msgb = gsup_client_msgb_alloc(); + if (!gsup_msgb) { + LOGP(DMM, LOGL_ERROR, "Couldn't allocate GSUP message\n"); + rc = -ENOMEM; + goto error; + } + + /* Encode GSUP message */ + rc = osmo_gsup_encode(gsup_msgb, &gsup_msg); + if (rc) { + LOGP(DMM, LOGL_ERROR, "Couldn't encode GSUP message\n"); + goto error; + } + + /* Finally send */ + rc = gsup_client_send(conn->network->vlr->gsup_client, gsup_msgb); + if (rc) { + LOGP(DMM, LOGL_ERROR, "Couldn't send GSUP message\n"); + goto error; + } + + /* Don't release connection, wait for response */ msc_subscr_conn_communicating(conn); - if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.ussd_text)) { - DEBUGP(DMM, "USSD: Own number requested\n"); - rc = send_own_number(conn, tid, req.invoke_id); - } else { - DEBUGP(DMM, "Unhandled USSD %s\n", req.ussd_text); - rc = gsm0480_send_ussd_return_error(conn, - tid, req.invoke_id, - GSM0480_ERR_CODE_UNEXPECTED_DATA_VALUE); + + return 0; + +error: + /* Abort transaction on DTAP-interface */ + gsm0480_send_ussd_reject(conn, tid, -1, + GSM_0480_PROBLEM_CODE_TAG_GENERAL, + GSM_0480_GEN_PROB_CODE_UNRECOGNISED); + if (trans) + trans_free(trans); + + /* TODO: abort transaction on GSUP interface if any */ + return rc; +} + +int gsm0911_gsup_handler(struct vlr_subscr *vsub, + struct osmo_gsup_message *gsup_msg) +{ + struct vlr_instance *vlr; + struct gsm_network *net; + struct gsm_trans *trans; + struct gsm48_hdr *gh; + struct msgb *ss_msg; + bool trans_end; + + /* Associate logging messages with this subscriber */ + log_set_context(LOG_CTX_VLR_SUBSCR, vsub); + + /* Obtain pointer to vlr_instance */ + vlr = vsub->vlr; + OSMO_ASSERT(vlr); + + /* Obtain pointer to gsm_network */ + net = (struct gsm_network *) vlr->user_ctx; + OSMO_ASSERT(net); + + /* Handle errors */ + if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) { + /* FIXME: handle this error somehow! */ + return 0; + } + + /* Attempt to find DTAP-transaction */ + trans = trans_find_by_callref(net, gsup_msg->session_id); + if (!trans) { + /* FIXME: network-originated sessions are not supported yet */ + LOGP(DMM, LOGL_ERROR, "Network-originated sessions " + "are not supported, dropping request...\n"); + return -ENOTSUP; } + /* Allocate and prepare a new MT message */ + ss_msg = gsm48_msgb_alloc_name("GSM 04.08 SS/USSD"); + gh = (struct gsm48_hdr *) msgb_push(ss_msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS; + gh->proto_discr |= trans->transaction_id << 4; + /** - * TODO: as we only handle *#100# for now, and always - * respond with RELEASE COMPLETE, let's manually free - * the transaction here, until the external interface - * is implemented. + * Perform GSUP-interface to A-interface mapping, + * according to GSM TS 09.11, table 4.1. + * + * TODO: see (note 3), both CONTINUE and END may + * be also mapped to REGISTER if a new transaction + * has to be established. */ - trans_free(trans); + switch (gsup_msg->session_state) { + case OSMO_GSUP_SESSION_STATE_BEGIN: + gh->msg_type = GSM0480_MTYPE_REGISTER; + break; + case OSMO_GSUP_SESSION_STATE_CONTINUE: + gh->msg_type = GSM0480_MTYPE_FACILITY; + break; + case OSMO_GSUP_SESSION_STATE_END: + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + break; - return rc; + /* Missing or incorrect session state */ + case OSMO_GSUP_SESSION_STATE_NONE: + default: + LOGP(DMM, LOGL_ERROR, "Unexpected session state %d\n", + gsup_msg->session_state); + /* FIXME: send ERROR back to the HLR */ + msgb_free(ss_msg); + return -EINVAL; + } + + /* Facility IE is optional only for RELEASE COMPLETE */ + if (gh->msg_type != GSM0480_MTYPE_RELEASE_COMPLETE) { + if (!gsup_msg->ss_info || gsup_msg->ss_info_len < 2) { + LOGP(DMM, LOGL_ERROR, "Missing mandatory Facility IE " + "for mapped 0x%02x message\n", gh->msg_type); + /* FIXME: send ERROR back to the HLR */ + msgb_free(ss_msg); + return -EINVAL; + } + } + + /* Append Facility IE if preset */ + if (gsup_msg->ss_info && gsup_msg->ss_info_len > 2) { + /* Facility IE carries LV, others carry TLV */ + if (gh->msg_type == GSM0480_MTYPE_FACILITY) + msgb_lv_put(ss_msg, gsup_msg->ss_info_len, gsup_msg->ss_info); + else + msgb_tlv_put(ss_msg, GSM0480_IE_FACILITY, + gsup_msg->ss_info_len, gsup_msg->ss_info); + } + + /* Should we release the transaction? */ + trans_end = (gh->msg_type == GSM0480_MTYPE_RELEASE_COMPLETE); + + /* Sent to the MS, give ownership of ss_msg */ + msc_tx_dtap(trans->conn, ss_msg); + + /* Release transaction if required */ + if (trans_end) + trans_free(trans); + + return 0; } diff --git a/tests/msc_vlr/msc_vlr_test_ss.c b/tests/msc_vlr/msc_vlr_test_ss.c index f3a1f68e5..f0652b07b 100644 --- a/tests/msc_vlr/msc_vlr_test_ss.c +++ b/tests/msc_vlr/msc_vlr_test_ss.c @@ -94,11 +94,21 @@ static void _test_ss_ussd(enum ran_type via_ran) EXPECT_ACCEPTED(true); /* MT: GSM 04.80 RELEASE COMPLETE with Facility IE */ + gsup_expect_tx("20" /* OSMO_GSUP_MSGT_PROC_SS_REQUEST */ + "0108" "09710000004026f0" /* IMSI TLV */ + "3004" "20000001" /* Session ID TLV */ + "3101" "01" /* Session state: BEGIN */ + "3515" FACILITY_IE_REQ); dtap_expect_tx("8b2a" "1c27" FACILITY_IE_RSP); expect_release_clear(via_ran); /* MO: GSM 04.80 REGISTER with Facility IE and SS version IE */ ms_sends_msg("0b7b" "1c15" FACILITY_IE_REQ "7f0100"); + gsup_rx("20" /* OSMO_GSUP_MSGT_PROC_SS_REQUEST */ + "0108" "09710000004026f0" /* IMSI TLV */ + "3004" "20000001" /* Session ID TLV */ + "3101" "03" /* Session state: END */ + "3527" FACILITY_IE_RSP, NULL); VERBOSE_ASSERT(dtap_tx_confirmed, == true, "%d"); ASSERT_RELEASE_CLEAR(via_ran); diff --git a/tests/msc_vlr/msc_vlr_test_ss.err b/tests/msc_vlr/msc_vlr_test_ss.err index c7f77119e..fce5bb6a6 100644 --- a/tests/msc_vlr/msc_vlr_test_ss.err +++ b/tests/msc_vlr/msc_vlr_test_ss.err @@ -162,22 +162,27 @@ DREF VLR subscr MSISDN:46071 usage increases to: 3 DREF MSISDN:46071: MSC conn use + trans_nc_ss == 3 (0x4a: dtap,cm_service,trans_nc_ss) DMM MSISDN:46071: rx msg GSM0480_MTYPE_REGISTER: received_cm_service_request changes to false DREF MSISDN:46071: MSC conn use - cm_service == 2 (0x42: dtap,trans_nc_ss) +GSUP --> HLR: OSMO_GSUP_MSGT_PROC_SS_REQUEST: 20010809710000004026f03004200000013101013515a11302010102013b300b04010f0406aa510c061b01 DMM Subscr_Conn(CM_SERVICE_REQ:901700000004620){SUBSCR_CONN_S_ACCEPTED}: Received Event SUBSCR_CONN_E_COMMUNICATING DMM Subscr_Conn(CM_SERVICE_REQ:901700000004620){SUBSCR_CONN_S_ACCEPTED}: state_chg to SUBSCR_CONN_S_COMMUNICATING -DMM USSD: Own number requested -DMM MSISDN:46071: MSISDN = 46071 +DREF MSISDN:46071: MSC conn use - dtap == 1 (0x40: trans_nc_ss) +<-- GSUP rx OSMO_GSUP_MSGT_PROC_SS_REQUEST: 20010809710000004026f03004200000013101033527a225020101302002013b301b04010f0416d9775d0e2ae3e965f73cfd7683d27310cd06bbc51a0d +DVLR GSUP rx 61: 20010809710000004026f03004200000013101033527a225020101302002013b301b04010f0416d9775d0e2ae3e965f73cfd7683d27310cd06bbc51a0d +DREF VLR subscr MSISDN:46071 usage increases to: 4 +DMSC Routed to GSM 09.11 SS/USSD handler DMSC msc_tx 43 bytes to MSISDN:46071 via RAN_GERAN_A - DTAP --RAN_GERAN_A--> MS: GSM0480_MTYPE_RELEASE_COMPLETE: 8b2a1c27a225020101302002013b301b04010f0416d9775d0e2ae3e965f73cfd7683d27310cd06bbc51a0d - DTAP matches expected message -DREF VLR subscr MSISDN:46071 usage decreases to: 2 -DREF MSISDN:46071: MSC conn use - trans_nc_ss == 1 (0x2: dtap) -DREF MSISDN:46071: MSC conn use - dtap == 0 (0x0: ) +DREF VLR subscr MSISDN:46071 usage decreases to: 3 +DREF MSISDN:46071: MSC conn use - trans_nc_ss == 0 (0x0: ) DMM Subscr_Conn(CM_SERVICE_REQ:901700000004620){SUBSCR_CONN_S_COMMUNICATING}: Received Event SUBSCR_CONN_E_UNUSED DMM Subscr_Conn(CM_SERVICE_REQ:901700000004620){SUBSCR_CONN_S_COMMUNICATING}: state_chg to SUBSCR_CONN_S_RELEASING DREF MSISDN:46071: MSC conn use + release == 1 (0x100: release) -DREF VLR subscr MSISDN:46071 usage increases to: 3 -DREF VLR subscr MSISDN:46071 usage decreases to: 2 +DREF VLR subscr MSISDN:46071 usage increases to: 4 +DREF VLR subscr MSISDN:46071 usage decreases to: 3 - BSSAP Clear --RAN_GERAN_A--> MS +DREF VLR subscr MSISDN:46071 usage decreases to: 2 +<-- GSUP rx OSMO_GSUP_MSGT_PROC_SS_REQUEST: vlr_gsupc_read_cb() returns 0 dtap_tx_confirmed == 1 bssap_clear_sent == 1 - all requests serviced, conn has been released |