From d3dbe75844f07c3605ea00840a9f88242f990a16 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 2 Jun 2010 12:42:37 +0200 Subject: USSD: Incomplete attempt to implement non-call SS interrogate --- openbsc/include/openbsc/gsm_04_80.h | 72 +++++++++++++++++++++++++++++++++++-- openbsc/src/gsm_04_80.c | 69 +++++++++++++++++++++++++++++++++-- openbsc/src/ussd.c | 72 +++++++++++++++++++++++++++---------- 3 files changed, 189 insertions(+), 24 deletions(-) diff --git a/openbsc/include/openbsc/gsm_04_80.h b/openbsc/include/openbsc/gsm_04_80.h index b5ab1c6ea..03a46fa69 100644 --- a/openbsc/include/openbsc/gsm_04_80.h +++ b/openbsc/include/openbsc/gsm_04_80.h @@ -4,12 +4,78 @@ #include #include +/* 29.002 V8.9.0 / 17.7.5 */ +enum gsm0902_map_ss_code { + /* line identification */ + MAP_SS_CODE_CLIP = 0x11, /* calling line id present */ + MAP_SS_CODE_CLIR = 0x12, /* calling line id restrict */ + MAP_SS_CODE_COLP = 0x13, /* connected line id present */ + MAP_SS_CODE_COLR = 0x14, /* onnected line id restrict */ + MAP_SS_CODE_MCI = 0x15, /* malicious call id */ + + /* name identification */ + MAP_SS_CODE_CNAP = 0x19, /* calling name presentation */ + + /* forwarding */ + MAP_SS_CODE_CFU = 0x21, /* call forw unconditional */ + MAP_SS_CODE_CFB = 0x29, /* call forw subscr busy */ + MAP_SS_CODE_CFNRY = 0x2a, /* call forw no reply */ + MAP_SS_CODE_CFNRC = 0x2b, /* call forw not reachable */ + MAP_SS_CODE_CD = 0x24, /* call deflection */ + + /* call offering */ + MAP_SS_CODE_ECT = 0x31, /* explicit call transfer */ + MAP_SS_CODE_MAH = 0x32, /* mobile access hunting */ + + /* call completion SS */ + MAP_SS_CODE_CW = 0x41, /* call waiting */ + MAP_SS_CODE_HOLD = 0x42, /* call hold */ + MAP_SS_CODE_CCBS_A = 0x43, + MAP_SS_CODE_CCBS_B = 0x44, + MAP_SS_CODE_MC = 0x45, /* multicall */ + + MAP_SS_CODE_MPTY = 0x51, /* multiparty */ + + MAP_SS_CODE_CUG = 0x61, /* closed user group */ + + MAP_SS_CODE_AOCI = 0x71, /* advice of charge info */ + MAP_SS_CODE_AOCC = 0x72, /* advice of charge charging */ + + MAP_SS_CODE_UUS1 = 0x81, + MAP_SS_CODE_UUS2 = 0x82, + MAP_SS_CODE_UUS3 = 0x83, + + /* barring */ + MAP_SS_CODE_BOOC = 0x91, + MAP_SS_CODE_BAOC = 0x92, /* barr all outgoing calls */ + MAP_SS_CODE_BOIC = 0x93, /* barr outgoing intl calls */ + MAP_SS_CODE_BAIC = 0x9a, /* barr all incoming calls */ + MAP_SS_CODE_BICROAM = 0x9b, /* barr incoming calls in roaming */ +}; + +/* SS-Status + 23.011 Section 2.1.2.1 Table 2.1 */ +#define SS_STATUS_Q_BIT 0x08 /* quiescent */ +#define SS_STATUS_P_BIT 0x04 /* provisioned */ +#define SS_STATUS_R_BIT 0x02 /* registered */ +#define SS_STATUS_A_BIT 0x01 /* active */ + + #define MAX_LEN_USSD_STRING 31 struct ussd_request { + uint8_t opcode; + + uint8_t transaction_id; + uint8_t invoke_id; + + union { + struct { char text[MAX_LEN_USSD_STRING + 1]; - u_int8_t transaction_id; - u_int8_t invoke_id; + } unstructured; + struct { + uint8_t ss_code; + } interrogate; + }; }; int gsm0480_decode_ussd_request(const struct msgb *msg, @@ -18,5 +84,7 @@ int gsm0480_send_ussd_response(const struct msgb *in_msg, const char* response_t const struct ussd_request *req); int gsm0480_send_ussd_reject(const struct msgb *msg, const struct ussd_request *request); +int gsm0480_send_ss_interr_resp(const struct msgb *in_msg, uint8_t status, + const struct ussd_request *req); #endif diff --git a/openbsc/src/gsm_04_80.c b/openbsc/src/gsm_04_80.c index ef10b1702..efb798ea5 100644 --- a/openbsc/src/gsm_04_80.c +++ b/openbsc/src/gsm_04_80.c @@ -47,6 +47,8 @@ static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length, struct ussd_request *req); static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length, struct ussd_request *req); +static int parse_process_ss_interr_req(uint8_t *uss_req_data, uint8_t length, + struct ussd_request *req); static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, u_int8_t tag) { @@ -95,7 +97,7 @@ static int parse_ussd(u_int8_t *ussd, struct ussd_request *req) case GSM0480_MTYPE_RELEASE_COMPLETE: DEBUGP(DMM, "USS Release Complete\n"); /* could also parse out the optional Cause/Facility data */ - req->text[0] = 0xFF; + req->unstructured.text[0] = 0xFF; break; case GSM0480_MTYPE_REGISTER: case GSM0480_MTYPE_FACILITY: @@ -194,12 +196,17 @@ static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length, /* mandatory part */ if (invoke_data[offset] == GSM0480_OPERATION_CODE) { u_int8_t operation_code = invoke_data[offset+2]; + req->opcode = operation_code; switch (operation_code) { case GSM0480_OP_CODE_PROCESS_USS_REQ: rc = parse_process_uss_req(invoke_data + offset + 3, length - offset - 3, req); break; + case GSM0480_OP_CODE_INTERROGATE_SS: + rc = parse_process_ss_interr_req(invoke_data + offset + 3, + length - offset - 3, req); + break; default: fprintf(stderr, "GSM 04.80 operation code 0x%02x " "is not yet handled\n", operation_code); @@ -233,10 +240,10 @@ static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length, /* Prevent a mobile-originated buffer-overrun! */ if (num_chars > MAX_LEN_USSD_STRING) num_chars = MAX_LEN_USSD_STRING; - gsm_7bit_decode(req->text, + gsm_7bit_decode(req->unstructured.text, &(uss_req_data[7]), num_chars); /* append null-terminator */ - req->text[num_chars+1] = 0; + req->unstructured.text[num_chars+1] = 0; rc = 1; } } @@ -244,6 +251,62 @@ static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length, return rc; } +static int parse_process_ss_interr_req(uint8_t *uss_req_data, uint8_t length, + struct ussd_request *req) +{ + int rc = -EIO; + + if (uss_req_data[0] != GSM_0480_SEQUENCE_TAG) + goto err; + + if (uss_req_data[2] != ASN1_OCTET_STRING_TAG) + goto err; + + req->interrogate.ss_code = uss_req_data[4]; + + return rc; +err: + return -EIO; +} + +/* Send response to a mobile-originated InterrogateSS-Request */ +int gsm0480_send_ss_interr_resp(const struct msgb *in_msg, uint8_t status, + const struct ussd_request *req) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + msg->lchan = in_msg->lchan; + + /* Pre-pend the ss-Status */ + msgb_push_TLV1(msg, 0, status); + + /* Pre-pend the operation code */ + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, + GSM0480_OP_CODE_INTERROGATE_SS); + + /* 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); + + /* Wrap this up as a Return Result component */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); + + /* 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 | req->transaction_id + | (1<<7); /* TI direction = 1 */ + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return gsm48_sendmsg(msg, NULL); +} + + /* Send response to a mobile-originated ProcessUnstructuredSS-Request */ int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_text, const struct ussd_request *req) diff --git a/openbsc/src/ussd.c b/openbsc/src/ussd.c index 547691965..58f51c869 100644 --- a/openbsc/src/ussd.c +++ b/openbsc/src/ussd.c @@ -37,9 +37,51 @@ /* 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(const struct msgb *msg, const struct ussd_request *req); +/* A network-specific handler function */ +static int send_own_number(const struct msgb *msg, const struct ussd_request *req) +{ + char *own_number = msg->lchan->conn.subscr->extension; + char response_string[GSM_EXTENSION_LENGTH + 20]; + /* Need trailing CR as EOT character */ + snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); + return gsm0480_send_ussd_response(msg, response_string, req); +} + +static int handle_rcv_uss_req(struct msgb *msg, struct ussd_request *req) +{ + if (req->unstructured.text[0] == 0xFF) /* Release-Complete */ + return 0; + + if (strstr(USSD_TEXT_OWN_NUMBER, req->unstructured.text) != NULL) { + LOGP(DMM, LOGL_INFO, "USSD: Own number requested\n"); + return send_own_number(msg, req); + } else { + LOGP(DMM, LOGL_NOTICE, "Unhandled USSD %s\n", req->unstructured.text); + return gsm0480_send_ussd_reject(msg, req); + } +} + +static int handle_rcv_interrogate(struct msgb *msg, struct ussd_request *req) +{ + uint8_t ss_status; + + switch (req->interrogate.ss_code) { + case MAP_SS_CODE_CLIP: + case MAP_SS_CODE_COLP: + ss_status = SS_STATUS_P_BIT | SS_STATUS_R_BIT | SS_STATUS_A_BIT; + break; + case MAP_SS_CODE_CLIR: + case MAP_SS_CODE_COLR: + ss_status = SS_STATUS_P_BIT | SS_STATUS_R_BIT; + break; + default: + ss_status = 0; + break; + } + + return gsm0480_send_ss_interr_resp(msg, ss_status, req); +} /* Entrypoint - handler function common to all mobile-originated USSDs */ int handle_rcv_ussd(struct msgb *msg) @@ -47,25 +89,17 @@ int handle_rcv_ussd(struct msgb *msg) struct ussd_request req; gsm0480_decode_ussd_request(msg, &req); - if (req.text[0] == 0xFF) /* Release-Complete */ - return 0; - if (strstr(USSD_TEXT_OWN_NUMBER, req.text) != NULL) { - DEBUGP(DMM, "USSD: Own number requested\n"); - return send_own_number(msg, &req); - } else { - DEBUGP(DMM, "Unhandled USSD %s\n", req.text); - return gsm0480_send_ussd_reject(msg, &req); + switch (req.opcode) { + case GSM0480_OP_CODE_INTERROGATE_SS: + return handle_rcv_interrogate(msg, &req); + case GSM0480_OP_CODE_USS_REQUEST: + return handle_rcv_uss_req(msg, &req); + default: + LOGP(DMM, LOGL_NOTICE, "Unknown SS opcode 0x%02x\n", req.opcode); + break; } + return 0; } -/* A network-specific handler function */ -static int send_own_number(const struct msgb *msg, const struct ussd_request *req) -{ - char *own_number = msg->lchan->conn.subscr->extension; - char response_string[GSM_EXTENSION_LENGTH + 20]; - /* Need trailing CR as EOT character */ - snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); - return gsm0480_send_ussd_response(msg, response_string, req); -} -- cgit v1.2.3