blob: d0dcbc2c39d235d2bbd9df4d873963b3a020b26a [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
60static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, u_int8_t tag, u_int8_t value)
61{
62 msgb->data -= 3;
63 msgb->len += 3;
64 msgb->data[0] = tag;
65 msgb->data[1] = 1;
66 msgb->data[2] = value;
67 return msgb->data;
68}
69
70
71/* Receive a mobile-originated USSD message and return the decoded text */
72char* gsm0480_rcv_ussd(struct msgb *msg)
73{
74 int rc = 0;
75 u_int8_t* parse_ptr = msgb_l3(msg);
76
77 memset(ussd_string_buff, 0, sizeof(ussd_string_buff));
78
79 if ((*parse_ptr & 0x0F) == GSM48_PDISC_NC_SS) {
80 last_transaction_id = *parse_ptr & 0x70;
81 rc = parse_ussd(parse_ptr + 1);
82 }
83
84 if (!rc)
85 DEBUGP(DMM, "Error occurred while parsing received USSD!\n");
86
87 return ussd_string_buff;
88}
89
90static int parse_ussd(u_int8_t *ussd)
91{
92 int rc = 1;
93 u_int8_t msg_type = ussd[0] & 0xBF; /* message-type - section 3.4 */
94
95 switch(msg_type) {
96 case GSM0480_MTYPE_RELEASE_COMPLETE:
97 DEBUGP(DMM, "USS Release Complete\n"); /* could also parse out the optional Cause/Facility data */
98 ussd_string_buff[0] = 0xFF;
99 break;
100 case GSM0480_MTYPE_REGISTER:
101 case GSM0480_MTYPE_FACILITY:
102 rc &= parse_ussd_information_elements(ussd+1);
103 break;
104 default:
105 fprintf(stderr, "Unknown GSM 04.80 message-type field 0x%02x\n",
106 ussd[0]);
107 rc = 0;
108 break;
109 }
110
111 return rc;
112}
113
114static int parse_ussd_information_elements(u_int8_t *ussd_ie)
115{
116 int rc;
117
118 u_int8_t iei = ussd_ie[0]; /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */
119 u_int8_t iei_length = ussd_ie[1];
120 switch(iei) {
121 case GSM48_IE_CAUSE:
122 break;
123 case GSM0480_IE_FACILITY:
124 rc = parse_facility_ie(ussd_ie+2, iei_length);
125 break;
126 case GSM0480_IE_SS_VERSION:
127 break;
128 default:
129 fprintf(stderr, "Unhandled GSM 04.08 or 04.80 Information Element Identifier 0x%02x\n",
130 iei);
131 rc = 0;
132 break;
133 }
134
135 return rc;
136}
137
138static int parse_facility_ie(u_int8_t *facility_ie, u_int8_t length)
139{
140 int rc = 1;
141 u_int8_t offset = 0;
142
143 do {
144 u_int8_t component_type = facility_ie[offset]; /* Component Type tag - table 3.7 */
145 u_int8_t component_length = facility_ie[offset+1];
146 switch(component_type) {
147 case GSM0480_CTYPE_INVOKE:
148 rc &= parse_ss_invoke(facility_ie+2, component_length);
149 break;
150 case GSM0480_CTYPE_RETURN_RESULT:
151 break;
152 case GSM0480_CTYPE_RETURN_ERROR:
153 break;
154 case GSM0480_CTYPE_REJECT:
155 break;
156 default:
157 fprintf(stderr, "Unknown GSM 04.80 Facility Component Type 0x%02x\n",
158 component_type);
159 rc = 0;
160 break;
161 }
162 offset += (component_length+2);
163 } while(offset < length);
164
165 return rc;
166}
167
168/* Parse an Invoke component - see table 3.3 */
169static int parse_ss_invoke(u_int8_t *invoke_data, u_int8_t length)
170{
171 int rc = 1;
172
173 if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) { /* mandatory part */
174 fprintf(stderr, "Unexpected GSM 04.80 Component-ID tag 0x%02x (expecting Invoke ID tag)\n",
175 invoke_data[0]);
176 }
177 u_int8_t offset = invoke_data[1] + 2;
178 last_invoke_id = invoke_data[2];
179
180 if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID) /* optional part */
181 offset += invoke_data[offset+1] + 2; /* skip over it */
182
183 if (invoke_data[offset] == GSM0480_OPERATION_CODE) { /* mandatory part */
184 u_int8_t operation_code = invoke_data[offset+2];
185 switch(operation_code) {
186 case GSM0480_OP_CODE_PROCESS_USS_REQ:
187 rc = parse_process_uss_req(invoke_data + offset + 3, length - offset - 3);
188 break;
189 default:
190 fprintf(stderr, "GSM 04.80 operation code 0x%02x is not yet handled\n",
191 operation_code);
192 rc = 0;
193 break;
194 }
195 } else {
196 fprintf(stderr, "Unexpected GSM 04.80 Component-ID tag 0x%02x (expecting Operation Code tag)\n",
197 invoke_data[0]);
198 rc = 0;
199 }
200
201 return rc;
202}
203
204/* Parse the parameters of a Process UnstructuredSS Request */
205static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length)
206{
207 int rc = 1;
208 int num_chars;
209 u_int8_t dcs;
210
211 /* FIXME: most phones send USSD text as a 7-bit encoded octet string; the following code
212 also handles the case of plain ASCII text (IA5String), but other encodings might be used */
213 if (uss_req_data[0] == GSM_0480_SEQUENCE_TAG) {
214 if (uss_req_data[2] == ASN1_OCTET_STRING_TAG) {
215 dcs = uss_req_data[4];
216 if ((dcs == 0x0F) && (uss_req_data[5] == ASN1_OCTET_STRING_TAG)) {
217 num_chars = (uss_req_data[6] * 8) / 7;
218 gsm_7bit_decode(ussd_string_buff, &(uss_req_data[7]), num_chars);
219 }
220 }
221 } else if (uss_req_data[0] == ASN1_IA5_STRING_TAG) {
222 num_chars = uss_req_data[1];
223 memcpy(ussd_string_buff, &(uss_req_data[2]), num_chars);
224 }
225
226 return rc;
227}
228
229/* Send response to a mobile-originated ProcessUnstructuredSS-Request */
230int gsm0480_send_ussd_response(struct msgb *in_msg, const char* response_text)
231{
232 struct msgb *msg = gsm48_msgb_alloc();
233 struct gsm48_hdr *gh;
234 u_int8_t *ptr8;
235 int response_len;
236
237 response_len = (strlen(response_text) * 7) / 8;
238 if (((strlen(response_text) * 7) % 8) != 0)
239 response_len += 1;
240
241 msg->bts_link = in_msg->bts_link;
242 msg->lchan = in_msg->lchan;
243
244 /* First put the payload text into the message */
245 ptr8 = msgb_put(msg, response_len);
246 gsm_7bit_encode(ptr8, response_text);
247
248 /* Then wrap it as an Octet String */
249 msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG);
250
251 /* Pre-pend the DCS octet string */
252 msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F);
253
254 /* Then wrap these as a Sequence */
255 msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
256
257 /* Pre-pend the operation code */
258 msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, GSM0480_OP_CODE_PROCESS_USS_REQ);
259
260 /* Wrap the operation code and IA5 string as a sequence */
261 msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
262
263 /* Pre-pend the invoke ID */
264 msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, last_invoke_id);
265
266 /* Wrap this up as a Return Result component */
267 msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT);
268
269 /* Wrap the component in a Facility message */
270 msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
271
272 /* And finally pre-pend the L3 header */
273 gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
274 gh->proto_discr = GSM48_PDISC_NC_SS | last_transaction_id | (1<<7); /* TI direction = 1 */
275 gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
276
277 return gsm48_sendmsg(msg, NULL);
278}
279
280int gsm0480_send_ussd_reject(struct msgb *in_msg)
281{
282 struct msgb *msg = gsm48_msgb_alloc();
283 struct gsm48_hdr *gh;
284
285 msg->bts_link = in_msg->bts_link;
286 msg->lchan = in_msg->lchan;
287
288 /* First insert the problem code */
289 msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
290
291 /* Before it insert the invoke ID */
292 msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, last_invoke_id);
293
294 /* Wrap this up as a Reject component */
295 msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT);
296
297 /* Wrap the component in a Facility message */
298 msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
299
300 /* And finally pre-pend the L3 header */
301 gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
302 gh->proto_discr = GSM48_PDISC_NC_SS | last_transaction_id | (1<<7); /* TI direction = 1 */
303 gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
304
305 return gsm48_sendmsg(msg, NULL);
306}