blob: 052b0a77ad8795809b926ec655a60964d99c59c2 [file] [log] [blame]
Harald Welte6eafe912009-10-16 08:32:58 +02001/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
2 * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
3
4/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
5 * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * (C) 2009 by Mike Haben <michael.haben@btinternet.com>
7 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 *
24 */
25
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <errno.h>
31
32#include <openbsc/msgb.h>
33#include <openbsc/tlv.h>
34#include <openbsc/debug.h>
35#include <openbsc/gsm_data.h>
36#include <openbsc/gsm_utils.h>
37#include <openbsc/gsm_04_08.h>
38#include <openbsc/gsm_04_80.h>
39
40static char ussd_string_buff[32];
41static u_int8_t last_transaction_id;
42static u_int8_t last_invoke_id;
43
44/* Forward declarations */
45static int parse_ussd(u_int8_t *ussd);
46static int parse_ussd_information_elements(u_int8_t *ussd_ie);
47static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length);
48static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length);
49static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length);
50
51static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, u_int8_t tag)
52{
53 msgb->data -= 2;
54 msgb->data[0] = tag;
55 msgb->data[1] = msgb->len;
56 msgb->len += 2;
57 return msgb->data;
58}
59
Harald Welte6307b852009-10-16 08:41:51 +020060static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, u_int8_t tag,
61 u_int8_t value)
Harald Welte6eafe912009-10-16 08:32:58 +020062{
63 msgb->data -= 3;
64 msgb->len += 3;
65 msgb->data[0] = tag;
66 msgb->data[1] = 1;
67 msgb->data[2] = value;
68 return msgb->data;
69}
70
71
72/* Receive a mobile-originated USSD message and return the decoded text */
Harald Welte6307b852009-10-16 08:41:51 +020073char *gsm0480_rcv_ussd(struct msgb *msg)
Harald Welte6eafe912009-10-16 08:32:58 +020074{
75 int rc = 0;
Harald Welte6307b852009-10-16 08:41:51 +020076 u_int8_t *parse_ptr = msgb_l3(msg);
Harald Welte6eafe912009-10-16 08:32:58 +020077
78 memset(ussd_string_buff, 0, sizeof(ussd_string_buff));
79
80 if ((*parse_ptr & 0x0F) == GSM48_PDISC_NC_SS) {
Harald Welte6307b852009-10-16 08:41:51 +020081 last_transaction_id = *parse_ptr & 0x70;
82 rc = parse_ussd(parse_ptr + 1);
Harald Welte6eafe912009-10-16 08:32:58 +020083 }
84
85 if (!rc)
86 DEBUGP(DMM, "Error occurred while parsing received USSD!\n");
87
88 return ussd_string_buff;
89}
90
91static int parse_ussd(u_int8_t *ussd)
92{
93 int rc = 1;
94 u_int8_t msg_type = ussd[0] & 0xBF; /* message-type - section 3.4 */
95
Harald Welte6307b852009-10-16 08:41:51 +020096 switch (msg_type) {
Harald Welte6eafe912009-10-16 08:32:58 +020097 case GSM0480_MTYPE_RELEASE_COMPLETE:
Harald Welte6307b852009-10-16 08:41:51 +020098 DEBUGP(DMM, "USS Release Complete\n");
99 /* could also parse out the optional Cause/Facility data */
Harald Welte6eafe912009-10-16 08:32:58 +0200100 ussd_string_buff[0] = 0xFF;
101 break;
102 case GSM0480_MTYPE_REGISTER:
103 case GSM0480_MTYPE_FACILITY:
104 rc &= parse_ussd_information_elements(ussd+1);
105 break;
106 default:
107 fprintf(stderr, "Unknown GSM 04.80 message-type field 0x%02x\n",
108 ussd[0]);
109 rc = 0;
110 break;
111 }
112
113 return rc;
114}
115
116static int parse_ussd_information_elements(u_int8_t *ussd_ie)
117{
118 int rc;
Harald Welte6307b852009-10-16 08:41:51 +0200119 /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */
120 u_int8_t iei = ussd_ie[0];
Harald Welte6eafe912009-10-16 08:32:58 +0200121 u_int8_t iei_length = ussd_ie[1];
Harald Welte6307b852009-10-16 08:41:51 +0200122
123 switch (iei) {
Harald Welte6eafe912009-10-16 08:32:58 +0200124 case GSM48_IE_CAUSE:
125 break;
126 case GSM0480_IE_FACILITY:
127 rc = parse_facility_ie(ussd_ie+2, iei_length);
128 break;
129 case GSM0480_IE_SS_VERSION:
130 break;
131 default:
Harald Welte6307b852009-10-16 08:41:51 +0200132 fprintf(stderr, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n",
Harald Welte6eafe912009-10-16 08:32:58 +0200133 iei);
134 rc = 0;
135 break;
136 }
137
138 return rc;
139}
140
141static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length)
142{
143 int rc = 1;
144 u_int8_t offset = 0;
145
146 do {
Harald Welte6307b852009-10-16 08:41:51 +0200147 /* Component Type tag - table 3.7 */
148 u_int8_t component_type = facility_ie[offset];
Harald Welte6eafe912009-10-16 08:32:58 +0200149 u_int8_t component_length = facility_ie[offset+1];
Harald Welte6307b852009-10-16 08:41:51 +0200150
151 switch (component_type) {
Harald Welte6eafe912009-10-16 08:32:58 +0200152 case GSM0480_CTYPE_INVOKE:
153 rc &= parse_ss_invoke(facility_ie+2, component_length);
154 break;
155 case GSM0480_CTYPE_RETURN_RESULT:
156 break;
157 case GSM0480_CTYPE_RETURN_ERROR:
158 break;
159 case GSM0480_CTYPE_REJECT:
160 break;
161 default:
Harald Welte6307b852009-10-16 08:41:51 +0200162 fprintf(stderr, "Unknown GSM 04.80 Facility "
163 "Component Type 0x%02x\n", component_type);
Harald Welte6eafe912009-10-16 08:32:58 +0200164 rc = 0;
165 break;
166 }
167 offset += (component_length+2);
Harald Welte6307b852009-10-16 08:41:51 +0200168 } while (offset < length);
Harald Welte6eafe912009-10-16 08:32:58 +0200169
170 return rc;
171}
172
173/* Parse an Invoke component - see table 3.3 */
174static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length)
175{
176 int rc = 1;
Harald Welte6307b852009-10-16 08:41:51 +0200177 u_int8_t offset;
178
179 /* mandatory part */
180 if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) {
181 fprintf(stderr, "Unexpected GSM 04.80 Component-ID tag "
182 "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]);
Harald Welte6eafe912009-10-16 08:32:58 +0200183 }
Harald Welte6307b852009-10-16 08:41:51 +0200184
185 offset = invoke_data[1] + 2;
Harald Welte6eafe912009-10-16 08:32:58 +0200186 last_invoke_id = invoke_data[2];
187
Harald Welte6307b852009-10-16 08:41:51 +0200188 /* optional part */
189 if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID)
Harald Welte6eafe912009-10-16 08:32:58 +0200190 offset += invoke_data[offset+1] + 2; /* skip over it */
Harald Welte6307b852009-10-16 08:41:51 +0200191
192 /* mandatory part */
193 if (invoke_data[offset] == GSM0480_OPERATION_CODE) {
Harald Welte6eafe912009-10-16 08:32:58 +0200194 u_int8_t operation_code = invoke_data[offset+2];
Harald Welte6307b852009-10-16 08:41:51 +0200195 switch (operation_code) {
Harald Welte6eafe912009-10-16 08:32:58 +0200196 case GSM0480_OP_CODE_PROCESS_USS_REQ:
Harald Welte6307b852009-10-16 08:41:51 +0200197 rc = parse_process_uss_req(invoke_data + offset + 3,
198 length - offset - 3);
Harald Welte6eafe912009-10-16 08:32:58 +0200199 break;
200 default:
Harald Welte6307b852009-10-16 08:41:51 +0200201 fprintf(stderr, "GSM 04.80 operation code 0x%02x "
202 "is not yet handled\n", operation_code);
Harald Welte6eafe912009-10-16 08:32:58 +0200203 rc = 0;
204 break;
205 }
206 } else {
Harald Welte6307b852009-10-16 08:41:51 +0200207 fprintf(stderr, "Unexpected GSM 04.80 Component-ID tag 0x%02x "
208 "(expecting Operation Code tag)\n",
Harald Welte6eafe912009-10-16 08:32:58 +0200209 invoke_data[0]);
210 rc = 0;
211 }
212
213 return rc;
214}
215
216/* Parse the parameters of a Process UnstructuredSS Request */
217static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length)
218{
219 int rc = 1;
220 int num_chars;
221 u_int8_t dcs;
222
Harald Welte6307b852009-10-16 08:41:51 +0200223 /* FIXME: most phones send USSD text as a 7-bit encoded octet string;
224 * the following code also handles the case of plain ASCII text
225 * (IA5String), but other encodings might be used */
226
Harald Welte6eafe912009-10-16 08:32:58 +0200227 if (uss_req_data[0] == GSM_0480_SEQUENCE_TAG) {
228 if (uss_req_data[2] == ASN1_OCTET_STRING_TAG) {
229 dcs = uss_req_data[4];
Harald Welte6307b852009-10-16 08:41:51 +0200230 if ((dcs == 0x0F) &&
231 (uss_req_data[5] == ASN1_OCTET_STRING_TAG)) {
Harald Welte6eafe912009-10-16 08:32:58 +0200232 num_chars = (uss_req_data[6] * 8) / 7;
Harald Welte6307b852009-10-16 08:41:51 +0200233 gsm_7bit_decode(ussd_string_buff,
234 &(uss_req_data[7]), num_chars);
Harald Welte6eafe912009-10-16 08:32:58 +0200235 }
236 }
237 } else if (uss_req_data[0] == ASN1_IA5_STRING_TAG) {
238 num_chars = uss_req_data[1];
239 memcpy(ussd_string_buff, &(uss_req_data[2]), num_chars);
240 }
241
242 return rc;
243}
244
245/* Send response to a mobile-originated ProcessUnstructuredSS-Request */
246int gsm0480_send_ussd_response(struct msgb *in_msg, const char* response_text)
247{
248 struct msgb *msg = gsm48_msgb_alloc();
249 struct gsm48_hdr *gh;
250 u_int8_t *ptr8;
251 int response_len;
252
253 response_len = (strlen(response_text) * 7) / 8;
254 if (((strlen(response_text) * 7) % 8) != 0)
255 response_len += 1;
256
257 msg->bts_link = in_msg->bts_link;
258 msg->lchan = in_msg->lchan;
259
260 /* First put the payload text into the message */
261 ptr8 = msgb_put(msg, response_len);
262 gsm_7bit_encode(ptr8, response_text);
263
264 /* Then wrap it as an Octet String */
265 msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG);
266
267 /* Pre-pend the DCS octet string */
268 msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F);
269
270 /* Then wrap these as a Sequence */
271 msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
272
273 /* Pre-pend the operation code */
Harald Welte6307b852009-10-16 08:41:51 +0200274 msgb_push_TLV1(msg, GSM0480_OPERATION_CODE,
275 GSM0480_OP_CODE_PROCESS_USS_REQ);
Harald Welte6eafe912009-10-16 08:32:58 +0200276
277 /* Wrap the operation code and IA5 string as a sequence */
278 msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
279
280 /* Pre-pend the invoke ID */
281 msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, last_invoke_id);
282
283 /* Wrap this up as a Return Result component */
284 msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT);
285
286 /* Wrap the component in a Facility message */
287 msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
288
289 /* And finally pre-pend the L3 header */
290 gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
291 gh->proto_discr = GSM48_PDISC_NC_SS | last_transaction_id | (1<<7); /* TI direction = 1 */
292 gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
293
294 return gsm48_sendmsg(msg, NULL);
295}
296
297int gsm0480_send_ussd_reject(struct msgb *in_msg)
298{
299 struct msgb *msg = gsm48_msgb_alloc();
300 struct gsm48_hdr *gh;
301
302 msg->bts_link = in_msg->bts_link;
303 msg->lchan = in_msg->lchan;
304
305 /* First insert the problem code */
Harald Welte6307b852009-10-16 08:41:51 +0200306 msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL,
307 GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
Harald Welte6eafe912009-10-16 08:32:58 +0200308
309 /* Before it insert the invoke ID */
310 msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, last_invoke_id);
311
312 /* Wrap this up as a Reject component */
313 msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT);
314
315 /* Wrap the component in a Facility message */
316 msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
317
318 /* And finally pre-pend the L3 header */
319 gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
Harald Welte6307b852009-10-16 08:41:51 +0200320 gh->proto_discr = GSM48_PDISC_NC_SS;
321 gh->proto_discr |= last_transaction_id | (1<<7); /* TI direction = 1 */
Harald Welte6eafe912009-10-16 08:32:58 +0200322 gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
323
324 return gsm48_sendmsg(msg, NULL);
325}