diff options
Diffstat (limited to 'openbsc/src/libmsc/ussd.c')
-rw-r--r-- | openbsc/src/libmsc/ussd.c | 249 |
1 files changed, 243 insertions, 6 deletions
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 |