blob: 7756ecba897be90580ee07d593c5b80bc460a0c5 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file gsm0480.c
2 * Format functions for GSM 04.80. */
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +08003/*
4 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2009 by Mike Haben <michael.haben@btinternet.com>
Harald Welteb1a35d62018-06-16 18:34:52 +02006 * (C) 2018 by Harald Welte <laforge@gnumonks.org>
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +08007 *
8 * All Rights Reserved
9 *
Harald Weltee08da972017-11-13 01:00:26 +090010 * SPDX-License-Identifier: GPL-2.0+
11 *
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +080012 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 *
26 */
27
Vadim Yanitskiy30cfeeb2018-08-03 05:44:00 +070028#include <osmocom/gsm/gsm48.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010029#include <osmocom/gsm/gsm0480.h>
30#include <osmocom/gsm/gsm_utils.h>
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +080031
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010032#include <osmocom/core/logging.h>
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +080033
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010034#include <osmocom/gsm/protocol/gsm_04_08.h>
35#include <osmocom/gsm/protocol/gsm_04_80.h>
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +080036
37#include <string.h>
Vadim Yanitskiy52e44122018-06-11 03:51:11 +070038#include <errno.h>
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +080039
Harald Welteb1a35d62018-06-16 18:34:52 +020040const struct value_string gsm0480_comp_type_names[] = {
41 { GSM0480_CTYPE_INVOKE, "Invoke" },
42 { GSM0480_CTYPE_RETURN_RESULT, "ReturnResult" },
43 { GSM0480_CTYPE_RETURN_ERROR, "ReturnError" },
44 { GSM0480_CTYPE_REJECT, "Reject" },
45 { 0, NULL }
46};
47
48const struct value_string gsm0480_op_code_names[] = {
49 { GSM0480_OP_CODE_REGISTER_SS, "RegisterSS" },
50 { GSM0480_OP_CODE_ERASE_SS, "EraseSS" },
51 { GSM0480_OP_CODE_ACTIVATE_SS, "ActivateSS" },
52 { GSM0480_OP_CODE_DEACTIVATE_SS, "DeactivateSS" },
53 { GSM0480_OP_CODE_INTERROGATE_SS, "IngerrogateSS" },
54 { GSM0480_OP_CODE_NOTIFY_SS, "NotifySS" },
55 { GSM0480_OP_CODE_REGISTER_PASSWORD, "RegisterPassword" },
56 { GSM0480_OP_CODE_GET_PASSWORD, "GetPassword" },
57 { GSM0480_OP_CODE_PROCESS_USS_DATA, "ProcessUSSD" },
58 { GSM0480_OP_CODE_FORWARD_CHECK_SS_IND, "ForwardChecckSSind" },
59 { GSM0480_OP_CODE_PROCESS_USS_REQ, "ProcessUssReq" },
60 { GSM0480_OP_CODE_USS_REQUEST, "UssRequest" },
61 { GSM0480_OP_CODE_USS_NOTIFY, "UssNotify" },
62 { GSM0480_OP_CODE_FORWARD_CUG_INFO, "ForwardCugInfo" },
63 { GSM0480_OP_CODE_SPLIT_MPTY, "SplitMPTY" },
64 { GSM0480_OP_CODE_RETRIEVE_MPTY, "RetrieveMPTY" },
65 { GSM0480_OP_CODE_HOLD_MPTY, "HoldMPTY" },
66 { GSM0480_OP_CODE_BUILD_MPTY, "BuildMPTY" },
67 { GSM0480_OP_CODE_FORWARD_CHARGE_ADVICE, "ForwardChargeAdvice" },
68 { 0, NULL }
69};
70
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +080071static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag)
72{
73 uint8_t *data = msgb_push(msgb, 2);
74
75 data[0] = tag;
76 data[1] = msgb->len - 2;
77 return data;
78}
79
80static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag,
81 uint8_t value)
82{
83 uint8_t *data = msgb_push(msgb, 3);
84
85 data[0] = tag;
86 data[1] = 1;
87 data[2] = value;
88 return data;
89}
90
Harald Welte7ecc4a32018-07-28 23:05:36 +020091static inline unsigned char *msgb_push_NULL(struct msgb *msgb)
92{
93 uint8_t *data = msgb_push(msgb, 2);
94
95 data[0] = ASN1_NULL_TYPE_TAG;
96 data[1] = 0;
97 return data;
98}
99
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +0800100/* wrap an invoke around it... the other way around
101 *
102 * 1.) Invoke Component tag
103 * 2.) Invoke ID Tag
104 * 3.) Operation
105 * 4.) Data
106 */
107int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id)
108{
109 /* 3. operation */
110 msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, op);
111
112 /* 2. invoke id tag */
113 msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, link_id);
114
115 /* 1. component tag */
116 msgb_wrap_with_TL(msg, GSM0480_CTYPE_INVOKE);
117
118 return 0;
119}
120
121/* wrap the GSM 04.08 Facility IE around it */
122int gsm0480_wrap_facility(struct msgb *msg)
123{
124 msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
125
126 return 0;
127}
128
129struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text)
130{
131 struct msgb *msg;
132 uint8_t *seq_len_ptr, *ussd_len_ptr, *data;
Holger Hans Peter Freyther47aa4822013-07-07 13:54:53 +0200133 int len;
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +0800134
Vadim Yanitskiy39a36d02018-08-03 03:52:16 +0700135 msg = gsm0480_msgb_alloc_name("TS 04.80 USSD Notify");
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +0800136 if (!msg)
137 return NULL;
138
139 /* SEQUENCE { */
140 msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG);
141 seq_len_ptr = msgb_put(msg, 1);
142
143 /* DCS { */
144 msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
145 msgb_put_u8(msg, 1);
146 msgb_put_u8(msg, 0x0F);
147 /* } DCS */
148
149 /* USSD-String { */
150 msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
151 ussd_len_ptr = msgb_put(msg, 1);
152 data = msgb_put(msg, 0);
Jacob Erlbeck1d7f3b52013-08-12 17:07:53 +0200153 gsm_7bit_encode_n_ussd(data, msgb_tailroom(msg), text, &len);
Holger Hans Peter Freyther47aa4822013-07-07 13:54:53 +0200154 msgb_put(msg, len);
155 ussd_len_ptr[0] = len;
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +0800156 /* USSD-String } */
157
158 /* alertingPattern { */
159 msgb_put_u8(msg, ASN1_OCTET_STRING_TAG);
160 msgb_put_u8(msg, 1);
161 msgb_put_u8(msg, alertPattern);
162 /* } alertingPattern */
163
164 seq_len_ptr[0] = 3 + 2 + ussd_len_ptr[0] + 3;
165 /* } SEQUENCE */
166
167 return msg;
168}
169
170struct msgb *gsm0480_create_notifySS(const char *text)
171{
172 struct msgb *msg;
173 uint8_t *data, *tmp_len;
174 uint8_t *seq_len_ptr, *cal_len_ptr, *opt_len_ptr, *nam_len_ptr;
Holger Hans Peter Freyther47aa4822013-07-07 13:54:53 +0200175 int len;
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +0800176
177 len = strlen(text);
178 if (len < 1 || len > 160)
179 return NULL;
180
Vadim Yanitskiy39a36d02018-08-03 03:52:16 +0700181 msg = gsm0480_msgb_alloc_name("TS 04.80 NotifySS");
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +0800182 if (!msg)
183 return NULL;
184
185 msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG);
186 seq_len_ptr = msgb_put(msg, 1);
187
188 /* ss_code for CNAP { */
189 msgb_put_u8(msg, 0x81);
190 msgb_put_u8(msg, 1);
191 msgb_put_u8(msg, 0x19);
192 /* } ss_code */
193
194
195 /* nameIndicator { */
196 msgb_put_u8(msg, 0xB4);
197 nam_len_ptr = msgb_put(msg, 1);
198
199 /* callingName { */
200 msgb_put_u8(msg, 0xA0);
201 opt_len_ptr = msgb_put(msg, 1);
202 msgb_put_u8(msg, 0xA0);
203 cal_len_ptr = msgb_put(msg, 1);
204
205 /* namePresentationAllowed { */
206 /* add the DCS value */
207 msgb_put_u8(msg, 0x80);
208 msgb_put_u8(msg, 1);
209 msgb_put_u8(msg, 0x0F);
210
211 /* add the lengthInCharacters */
212 msgb_put_u8(msg, 0x81);
213 msgb_put_u8(msg, 1);
214 msgb_put_u8(msg, strlen(text));
215
216 /* add the actual string */
217 msgb_put_u8(msg, 0x82);
218 tmp_len = msgb_put(msg, 1);
219 data = msgb_put(msg, 0);
Jacob Erlbeck1d7f3b52013-08-12 17:07:53 +0200220 gsm_7bit_encode_n_ussd(data, msgb_tailroom(msg), text, &len);
Holger Hans Peter Freyther47aa4822013-07-07 13:54:53 +0200221 tmp_len[0] = len;
222 msgb_put(msg, len);
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +0800223
224 /* }; namePresentationAllowed */
225
Holger Hans Peter Freyther47aa4822013-07-07 13:54:53 +0200226 cal_len_ptr[0] = 3 + 3 + 2 + len;
Holger Hans Peter Freyther55aea502010-09-30 18:30:41 +0800227 opt_len_ptr[0] = cal_len_ptr[0] + 2;
228 /* }; callingName */
229
230 nam_len_ptr[0] = opt_len_ptr[0] + 2;
231 /* ); nameIndicator */
232
233 /* write the lengths... */
234 seq_len_ptr[0] = 3 + nam_len_ptr[0] + 2;
235
236 return msg;
237}
238
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800239/* Forward declarations */
Tobias Engel419684e2012-03-08 13:31:52 +0100240static int parse_ss(const struct gsm48_hdr *hdr,
241 uint16_t len, struct ss_request *req);
Vadim Yanitskiyc30431f2017-07-29 04:47:42 +0600242static int parse_ss_facility(const uint8_t *ss_facility, uint16_t len,
243 struct ss_request *req);
Vadim Yanitskiyb41c70f2018-01-17 12:10:07 +0600244static int parse_ss_info_elements(const uint8_t *ss_ie, uint16_t len,
Tobias Engel419684e2012-03-08 13:31:52 +0100245 struct ss_request *req);
Holger Hans Peter Freyther49ad5002010-10-11 09:06:47 +0200246static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
Tobias Engel419684e2012-03-08 13:31:52 +0100247 struct ss_request *req);
Vadim Yanitskiy7f16c442017-07-29 05:05:54 +0600248static int parse_ss_return_result(const uint8_t *rr_data, uint16_t length,
249 struct ss_request *req);
250static int parse_process_uss_data(const uint8_t *uss_req_data, uint16_t length,
251 struct ss_request *req);
Holger Hans Peter Freyther49ad5002010-10-11 09:06:47 +0200252static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
Tobias Engel419684e2012-03-08 13:31:52 +0100253 struct ss_request *req);
254static int parse_ss_for_bs_req(const uint8_t *ss_req_data,
255 uint16_t length,
256 struct ss_request *req);
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800257
Vadim Yanitskiy52e44122018-06-11 03:51:11 +0700258/*! Get pointer to the IE of a given type
259 * \param[in] hdr Pointer to the message starting from header
260 * \param[in] msg_len Length of the whole message + header
261 * \param[out] ie External pointer to be set
262 * \param[out] ie_len External IE length variable
263 * \param[in] ie_tag Tag value of the required IE
264 * \returns 0 in case of success, otherwise -ERRNO
265 *
266 * This function iterates over existing IEs within a given
267 * message (depending on its type), and looks for the one with
268 * given \ref ie_tag value. If the IE is found, the external
269 * pointer pointed by \ref ie will be set to its value part
270 * (omitting TL), and \ref ie_len will be set to the length.
271 * Otherwise, e.g. in case of parsing error, both \ref ie
272 * and \ref ie_len are set to NULL and 0 respectively.
273 */
274int gsm0480_extract_ie_by_tag(const struct gsm48_hdr *hdr, uint16_t msg_len,
275 uint8_t **ie, uint16_t *ie_len, uint8_t ie_tag)
276{
277 uint8_t pdisc, msg_type;
278 uint8_t *tlv, len;
279
280 /* Init external variables */
281 *ie_len = 0;
282 *ie = NULL;
283
284 /* Drop incomplete / corrupted messages */
285 if (msg_len < sizeof(*hdr))
286 return -EINVAL;
287
288 pdisc = gsm48_hdr_pdisc(hdr);
289 msg_type = gsm48_hdr_msg_type(hdr);
290
291 /* Drop non-SS related messages */
292 if (pdisc != GSM48_PDISC_NC_SS)
293 return -EINVAL;
294
295 len = msg_len - sizeof(*hdr);
296 tlv = (uint8_t *) hdr->data;
297
298 /* Parse a message depending on its type */
299 switch (msg_type) {
300 /* See table 2.5: RELEASE COMPLETE message content */
301 case GSM0480_MTYPE_RELEASE_COMPLETE:
302 /* See tables 2.3 and 2.4: REGISTER message content */
303 case GSM0480_MTYPE_REGISTER:
304 /* Iterate over TLV-based IEs */
305 while (len > 2) {
306 if (tlv[0] == ie_tag) {
307 *ie_len = tlv[1];
308 *ie = tlv + 2;
309 return 0;
310 }
311
312 len -= tlv[1] + 2;
313 tlv += tlv[1] + 2;
314 continue;
315 }
316
317 /* The Facility IE is mandatory for REGISTER */
318 if (msg_type == GSM0480_MTYPE_REGISTER)
319 if (ie_tag == GSM0480_IE_FACILITY)
320 return -EINVAL;
321 break;
322
323 /* See table 2.2: FACILITY message content */
324 case GSM0480_MTYPE_FACILITY:
325 /* There is no other IEs */
326 if (ie_tag != GSM0480_IE_FACILITY)
327 break;
328
329 /* Mandatory LV-based Facility IE */
330 if (len < 2)
331 return -EINVAL;
332
333 *ie_len = tlv[0];
334 *ie = tlv + 1;
335 return 0;
336
337 default:
338 /* Wrong message type, out of specs */
339 return -EINVAL;
340 }
341
342 return 0;
343}
344
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800345/* Decode a mobile-originated USSD-request message */
Holger Hans Peter Freytherdaa653f2010-10-11 07:56:06 +0200346int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len,
347 struct ussd_request *req)
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800348{
Tobias Engel419684e2012-03-08 13:31:52 +0100349 struct ss_request ss;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800350 int rc = 0;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800351
Tobias Engel419684e2012-03-08 13:31:52 +0100352 memset(&ss, 0, sizeof(ss));
353
Holger Hans Peter Freyther8ac04862010-10-11 08:08:58 +0200354 if (len < sizeof(*hdr) + 2) {
355 LOGP(0, LOGL_DEBUG, "USSD Request is too short.\n");
356 return 0;
357 }
358
Neels Hofmeyr282e9082016-03-14 16:06:46 +0100359 if (gsm48_hdr_pdisc(hdr) == GSM48_PDISC_NC_SS) {
Holger Hans Peter Freytherdaa653f2010-10-11 07:56:06 +0200360 req->transaction_id = hdr->proto_discr & 0x70;
Tobias Engel419684e2012-03-08 13:31:52 +0100361
362 ss.transaction_id = req->transaction_id;
Vadim Yanitskiy7689e0f2018-01-17 03:23:39 +0600363 rc = parse_ss(hdr, len - sizeof(*hdr), &ss);
Tobias Engel419684e2012-03-08 13:31:52 +0100364
365 /* convert from ss_request to legacy ussd_request */
366 req->transaction_id = ss.transaction_id;
367 req->invoke_id = ss.invoke_id;
368 if (ss.ussd_text[0] == 0xFF)
369 req->text[0] = '\0';
370 else {
371 memcpy(req->text, ss.ussd_text, sizeof(req->text));
372 req->text[sizeof(req->text)-1] = '\0';
373 }
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800374 }
375
376 if (!rc)
377 LOGP(0, LOGL_DEBUG, "Error occurred while parsing received USSD!\n");
378
379 return rc;
380}
381
Tobias Engel419684e2012-03-08 13:31:52 +0100382/* Decode a mobile-originated SS request message */
383int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len,
384 struct ss_request *req)
385{
Vadim Yanitskiyf07c58c2018-01-17 03:42:16 +0600386 uint8_t pdisc;
Tobias Engel419684e2012-03-08 13:31:52 +0100387
Vadim Yanitskiyf07c58c2018-01-17 03:42:16 +0600388 /**
389 * Check Protocol Discriminator
390 * see TS GSM 04.07 and GSM 04.80
391 */
392 pdisc = gsm48_hdr_pdisc(hdr);
393 if (pdisc != GSM48_PDISC_NC_SS) {
394 LOGP(0, LOGL_ERROR, "Dropping message with "
395 "unsupported pdisc=%02x\n", pdisc);
396 return 0;
Tobias Engel419684e2012-03-08 13:31:52 +0100397 }
398
Vadim Yanitskiyf07c58c2018-01-17 03:42:16 +0600399 /* GSM 04.80 3.3 Transaction Identifier */
400 req->transaction_id = hdr->proto_discr & 0x70;
Tobias Engel419684e2012-03-08 13:31:52 +0100401
Vadim Yanitskiyf07c58c2018-01-17 03:42:16 +0600402 /* Parse SS request */
403 return parse_ss(hdr, len - sizeof(*hdr), req);
Tobias Engel419684e2012-03-08 13:31:52 +0100404}
405
406static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *req)
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800407{
408 int rc = 1;
Neels Hofmeyra95ee5e2016-10-10 22:46:20 +0200409 uint8_t msg_type = hdr->msg_type & 0x3F; /* message-type - section 3.4 */
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800410
Vadim Yanitskiyfd744ce2018-01-17 03:31:15 +0600411 /**
412 * GSM 04.80 Section 2.5 'Release complete' Table 2.5
413 * payload is optional for 'RELEASE COMPLETE' message
414 */
415 if (msg_type != GSM0480_MTYPE_RELEASE_COMPLETE) {
416 if (len < 2) {
417 LOGP(0, LOGL_DEBUG, "SS Request is too short.\n");
418 return 0;
419 }
420 }
421
Vadim Yanitskiy44ebb932018-01-17 02:59:46 +0600422 /* Table 2.1: Messages for call independent SS control */
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800423 switch (msg_type) {
424 case GSM0480_MTYPE_RELEASE_COMPLETE:
Tobias Engel419684e2012-03-08 13:31:52 +0100425 LOGP(0, LOGL_DEBUG, "SS Release Complete\n");
Vadim Yanitskiy3cafc062018-01-17 12:28:40 +0600426
Vadim Yanitskiyfb5da892018-04-04 19:25:38 +0700427 /**
428 * Indicates that there is no decoded message.
429 * To be overwriten by the message otherwise.
430 */
431 req->ussd_text[0] = 0xFF;
432
Vadim Yanitskiy3cafc062018-01-17 12:28:40 +0600433 /* Parse optional Cause and/or Facility data */
434 if (len >= 2)
435 rc &= parse_ss_info_elements(&hdr->data[0], len, req);
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800436 break;
437 case GSM0480_MTYPE_REGISTER:
Vadim Yanitskiy7689e0f2018-01-17 03:23:39 +0600438 rc &= parse_ss_info_elements(&hdr->data[0], len, req);
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800439 break;
Vadim Yanitskiyc30431f2017-07-29 04:47:42 +0600440 case GSM0480_MTYPE_FACILITY:
Vadim Yanitskiy7689e0f2018-01-17 03:23:39 +0600441 rc &= parse_ss_facility(&hdr->data[0], len, req);
Vadim Yanitskiyc30431f2017-07-29 04:47:42 +0600442 break;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800443 default:
444 LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 message-type field 0x%02x\n",
Holger Hans Peter Freytherdaa653f2010-10-11 07:56:06 +0200445 hdr->msg_type);
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800446 rc = 0;
447 break;
448 }
449
450 return rc;
451}
452
Vadim Yanitskiyc30431f2017-07-29 04:47:42 +0600453static int parse_ss_facility(const uint8_t *ss_facility, uint16_t len,
454 struct ss_request *req)
455{
456 uint8_t facility_length;
457
458 facility_length = ss_facility[0];
459 if (len - 1 < facility_length)
460 return 0;
461
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700462 return !gsm0480_parse_facility_ie(ss_facility + 1, facility_length, req);
Vadim Yanitskiyc30431f2017-07-29 04:47:42 +0600463}
464
Tobias Engel419684e2012-03-08 13:31:52 +0100465static int parse_ss_info_elements(const uint8_t *ss_ie, uint16_t len,
466 struct ss_request *req)
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800467{
468 int rc = -1;
469 /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */
Holger Hans Peter Freytherdaa653f2010-10-11 07:56:06 +0200470 uint8_t iei;
471 uint8_t iei_length;
472
Vadim Yanitskiyb92a27f2018-01-17 12:18:27 +0600473 /* We need at least two bytes */
474 if (len < 2)
475 return 0;
476
Tobias Engel419684e2012-03-08 13:31:52 +0100477 iei = ss_ie[0];
478 iei_length = ss_ie[1];
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800479
Holger Hans Peter Freyther8ac04862010-10-11 08:08:58 +0200480 /* If the data does not fit, report an error */
Vadim Yanitskiyb92a27f2018-01-17 12:18:27 +0600481 if (iei_length + 2 > len)
Holger Hans Peter Freyther8ac04862010-10-11 08:08:58 +0200482 return 0;
483
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800484 switch (iei) {
485 case GSM48_IE_CAUSE:
486 break;
487 case GSM0480_IE_FACILITY:
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700488 rc = !gsm0480_parse_facility_ie(ss_ie + 2, iei_length, req);
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800489 break;
490 case GSM0480_IE_SS_VERSION:
491 break;
492 default:
493 LOGP(0, LOGL_DEBUG, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n",
494 iei);
495 rc = 0;
496 break;
497 }
498
Vadim Yanitskiyb92a27f2018-01-17 12:18:27 +0600499 /* A message may contain multiple IEs */
500 if (iei_length + 2 + 2 < len)
501 rc &= parse_ss_info_elements(ss_ie + iei_length + 2,
502 len - iei_length - 2, req);
503
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800504 return rc;
505}
506
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700507/*! Parse the components of a given Facility IE
508 * \param[in] facility_ie The Facility IE
509 * \param[in] length The length of Facility IE
510 * \param[out] req Abstract representation of SS message
511 * \return 0 in case of success, otherwise -ERRNO
512 */
513int gsm0480_parse_facility_ie(const uint8_t *facility_ie, uint16_t length,
514 struct ss_request *req)
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800515{
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700516 uint8_t component_length;
517 uint8_t component_type;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800518 uint8_t offset = 0;
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700519 int rc = 1;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800520
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700521 /* Iterate over components within IE */
Holger Hans Peter Freyther4156ec62010-10-11 09:07:50 +0200522 while (offset + 2 <= length) {
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800523 /* Component Type tag - table 3.7 */
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700524 component_type = facility_ie[offset];
525 component_length = facility_ie[offset + 1];
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800526
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700527 /* Make sure that there is no overflow */
Holger Hans Peter Freyther4156ec62010-10-11 09:07:50 +0200528 if (offset + 2 + component_length > length) {
529 LOGP(0, LOGL_ERROR, "Component does not fit.\n");
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700530 return -EINVAL;
Holger Hans Peter Freyther4156ec62010-10-11 09:07:50 +0200531 }
532
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800533 switch (component_type) {
534 case GSM0480_CTYPE_INVOKE:
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700535 rc &= parse_ss_invoke(facility_ie + 2,
Tobias Engel419684e2012-03-08 13:31:52 +0100536 component_length,
537 req);
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800538 break;
539 case GSM0480_CTYPE_RETURN_RESULT:
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700540 rc &= parse_ss_return_result(facility_ie + 2,
Vadim Yanitskiy7f16c442017-07-29 05:05:54 +0600541 component_length,
542 req);
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800543 break;
544 case GSM0480_CTYPE_RETURN_ERROR:
545 break;
546 case GSM0480_CTYPE_REJECT:
547 break;
548 default:
549 LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 Facility "
550 "Component Type 0x%02x\n", component_type);
551 rc = 0;
552 break;
553 }
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800554
Vadim Yanitskiy5a09f752018-06-11 04:58:53 +0700555 offset += (component_length + 2);
556 }
557
558 /**
559 * The internal functions are using inverted return
560 * codes, where '0' means error/failure. While a
561 * common approach is to return negative errno in
562 * case of any failure, and '0' if all is ok.
563 */
564 return (rc == 0) ? -EINVAL : 0;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800565}
566
567/* Parse an Invoke component - see table 3.3 */
Holger Hans Peter Freyther49ad5002010-10-11 09:06:47 +0200568static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length,
Tobias Engel419684e2012-03-08 13:31:52 +0100569 struct ss_request *req)
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800570{
571 int rc = 1;
572 uint8_t offset;
573
Holger Hans Peter Freyther7d0bce32010-10-11 09:12:33 +0200574 if (length < 3)
575 return 0;
576
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800577 /* mandatory part */
578 if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) {
579 LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag "
Tobias Engel419684e2012-03-08 13:31:52 +0100580 "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]);
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800581 }
582
583 offset = invoke_data[1] + 2;
584 req->invoke_id = invoke_data[2];
585
Holger Hans Peter Freyther7d0bce32010-10-11 09:12:33 +0200586 /* look ahead once */
587 if (offset + 1 > length)
588 return 0;
589
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800590 /* optional part */
591 if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID)
592 offset += invoke_data[offset+1] + 2; /* skip over it */
593
594 /* mandatory part */
595 if (invoke_data[offset] == GSM0480_OPERATION_CODE) {
Holger Hans Peter Freyther7d0bce32010-10-11 09:12:33 +0200596 if (offset + 2 > length)
597 return 0;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800598 uint8_t operation_code = invoke_data[offset+2];
Tobias Engel419684e2012-03-08 13:31:52 +0100599 req->opcode = operation_code;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800600 switch (operation_code) {
Vadim Yanitskiy511426d2017-07-29 05:11:39 +0600601 case GSM0480_OP_CODE_USS_NOTIFY:
602 case GSM0480_OP_CODE_USS_REQUEST:
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800603 case GSM0480_OP_CODE_PROCESS_USS_REQ:
604 rc = parse_process_uss_req(invoke_data + offset + 3,
605 length - offset - 3,
606 req);
607 break;
Vadim Yanitskiy394447b2017-07-29 05:14:15 +0600608 case GSM0480_OP_CODE_PROCESS_USS_DATA:
609 rc = parse_process_uss_data(invoke_data + offset + 3,
610 length - offset - 3,
611 req);
612 break;
Tobias Engel419684e2012-03-08 13:31:52 +0100613 case GSM0480_OP_CODE_ACTIVATE_SS:
614 case GSM0480_OP_CODE_DEACTIVATE_SS:
615 case GSM0480_OP_CODE_INTERROGATE_SS:
616 rc = parse_ss_for_bs_req(invoke_data + offset + 3,
617 length - offset - 3,
618 req);
619 break;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800620 default:
621 LOGP(0, LOGL_DEBUG, "GSM 04.80 operation code 0x%02x "
622 "is not yet handled\n", operation_code);
623 rc = 0;
624 break;
625 }
626 } else {
627 LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag 0x%02x "
628 "(expecting Operation Code tag)\n",
629 invoke_data[0]);
630 rc = 0;
631 }
632
633 return rc;
634}
635
Vadim Yanitskiy7f16c442017-07-29 05:05:54 +0600636/* Parse a Return Result component - see table 3.4 */
637static int parse_ss_return_result(const uint8_t *rr_data, uint16_t length,
638 struct ss_request *req)
639{
640 uint8_t operation_code;
641 uint8_t offset;
642
643 if (length < 3)
644 return 0;
645
646 /* Mandatory part */
647 if (rr_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) {
648 LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag "
649 "0x%02x (expecting Invoke ID tag)\n", rr_data[0]);
650 return 0;
651 }
652
653 offset = rr_data[1] + 2;
654 req->invoke_id = rr_data[2];
655
656 if (offset >= length)
657 return 0;
658
659 if (rr_data[offset] != GSM_0480_SEQUENCE_TAG)
660 return 0;
661
662 if (offset + 2 > length)
663 return 0;
664
665 offset += 2;
666 operation_code = rr_data[offset + 2];
667 req->opcode = operation_code;
668
669 switch (operation_code) {
670 case GSM0480_OP_CODE_USS_NOTIFY:
671 case GSM0480_OP_CODE_USS_REQUEST:
672 case GSM0480_OP_CODE_PROCESS_USS_REQ:
673 return parse_process_uss_req(rr_data + offset + 3,
674 length - offset - 3, req);
675 case GSM0480_OP_CODE_PROCESS_USS_DATA:
676 return parse_process_uss_data(rr_data + offset + 3,
677 length - offset - 3, req);
678 default:
679 LOGP(0, LOGL_DEBUG, "GSM 04.80 operation code 0x%02x "
680 "is not yet handled\n", operation_code);
681 return 0;
682 }
683
684 return 1;
685}
686
687static int parse_process_uss_data(const uint8_t *uss_req_data, uint16_t length,
688 struct ss_request *req)
689{
690 uint8_t num_chars;
691
692 /* we need at least that much */
693 if (length < 3)
694 return 0;
695
696 if (uss_req_data[0] != ASN1_IA5_STRING_TAG)
697 return 0;
698
699 num_chars = uss_req_data[1];
700 if (num_chars > length - 2)
701 return 0;
702
Vadim Yanitskiy2ecfb302018-04-04 19:19:07 +0700703 /* Drop messages with incorrect length */
704 if (num_chars > GSM0480_USSD_OCTET_STRING_LEN) {
705 LOGP(DLGLOBAL, LOGL_ERROR, "Incorrect USS_DATA data length=%u, "
706 "dropping message", num_chars);
707 return 0;
708 }
Vadim Yanitskiy7f16c442017-07-29 05:05:54 +0600709
710 memcpy(req->ussd_text, uss_req_data + 2, num_chars);
711
Vadim Yanitskiya24ead02018-04-04 10:34:41 +0700712 /* Copy the data 'as is' */
713 memcpy(req->ussd_data, uss_req_data + 2, num_chars);
714 req->ussd_data_len = num_chars;
715 req->ussd_data_dcs = 0x00;
716
Vadim Yanitskiy7f16c442017-07-29 05:05:54 +0600717 return 1;
718}
719
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800720/* Parse the parameters of a Process UnstructuredSS Request */
Holger Hans Peter Freyther49ad5002010-10-11 09:06:47 +0200721static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length,
Tobias Engel419684e2012-03-08 13:31:52 +0100722 struct ss_request *req)
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800723{
Vadim Yanitskiy5b0790d2017-07-29 04:26:21 +0600724 uint8_t num_chars;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800725 uint8_t dcs;
726
Holger Hans Peter Freytherd65a6982010-10-11 09:23:50 +0200727 /* we need at least that much */
728 if (length < 8)
729 return 0;
730
Vadim Yanitskiy5b0790d2017-07-29 04:26:21 +0600731 if (uss_req_data[0] != GSM_0480_SEQUENCE_TAG)
732 return 0;
Holger Hans Peter Freytherd65a6982010-10-11 09:23:50 +0200733
Vadim Yanitskiy5b0790d2017-07-29 04:26:21 +0600734 /* Both 2th and 5th should be equal to ASN1_OCTET_STRING_TAG */
735 if ((uss_req_data[2] & uss_req_data[5]) != ASN1_OCTET_STRING_TAG)
736 return 0;
737
Vadim Yanitskiy01b85722017-07-29 04:43:48 +0600738 /* Get DCS (Data Coding Scheme) */
Vadim Yanitskiy5b0790d2017-07-29 04:26:21 +0600739 dcs = uss_req_data[4];
Vadim Yanitskiya24ead02018-04-04 10:34:41 +0700740 /* Get the amount of bytes */
741 num_chars = uss_req_data[6];
742
Vadim Yanitskiy2ecfb302018-04-04 19:19:07 +0700743 /* Drop messages with incorrect length */
744 if (num_chars > GSM0480_USSD_OCTET_STRING_LEN) {
745 LOGP(DLGLOBAL, LOGL_ERROR, "Incorrect USS_REQ data length=%u, "
746 "dropping message", num_chars);
747 return 0;
748 }
Vadim Yanitskiya24ead02018-04-04 10:34:41 +0700749
750 /* Copy the data 'as is' */
751 memcpy(req->ussd_data, uss_req_data + 7, num_chars);
752 req->ussd_data_len = num_chars;
753 req->ussd_data_dcs = dcs;
Vadim Yanitskiy01b85722017-07-29 04:43:48 +0600754
755 /**
756 * According to GSM 04.08, 4.4.2 "ASN.1 data types":
757 * the USSD-DataCodingScheme shall indicate use of
758 * the default alphabet using the 0x0F value.
759 */
Vadim Yanitskiy5b0790d2017-07-29 04:26:21 +0600760 if (dcs == 0x0F) {
Vadim Yanitskiy01b85722017-07-29 04:43:48 +0600761 /* Calculate the amount of 7-bit characters */
Vadim Yanitskiya24ead02018-04-04 10:34:41 +0700762 num_chars = (num_chars * 8) / 7;
Vadim Yanitskiy01b85722017-07-29 04:43:48 +0600763
Vadim Yanitskiy5b0790d2017-07-29 04:26:21 +0600764 gsm_7bit_decode_n_ussd((char *)req->ussd_text,
765 sizeof(req->ussd_text), &(uss_req_data[7]), num_chars);
766
767 return 1;
Vadim Yanitskiy01b85722017-07-29 04:43:48 +0600768 } else {
Vadim Yanitskiy01b85722017-07-29 04:43:48 +0600769 memcpy(req->ussd_text, &(uss_req_data[7]), num_chars);
Vadim Yanitskiy01b85722017-07-29 04:43:48 +0600770 return 1;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800771 }
Vadim Yanitskiy5b0790d2017-07-29 04:26:21 +0600772
773 return 0;
Holger Hans Peter Freyther00cb5702010-10-09 01:47:15 +0800774}
Holger Hans Peter Freytherc64970e2010-10-18 16:56:43 +0200775
Tobias Engel419684e2012-03-08 13:31:52 +0100776/* Parse the parameters of a Interrogate/Activate/DeactivateSS Request */
777static int parse_ss_for_bs_req(const uint8_t *ss_req_data,
778 uint16_t length,
779 struct ss_request *req)
780{
781 int rc = 0;
782
783
784 /* we need at least that much */
785 if (length < 5)
786 return 0;
787
788
789 if (ss_req_data[0] == GSM_0480_SEQUENCE_TAG) {
790 if ((ss_req_data[2] == ASN1_OCTET_STRING_TAG) &&
791 ss_req_data[3] == 1) {
792 req->ss_code = ss_req_data[4];
793
794 rc = 1;
795 }
796 }
797 return rc;
798}
799
Harald Welte88fa5a32018-07-28 22:55:43 +0200800struct msgb *gsm0480_msgb_alloc_name(const char *name)
801{
802 return msgb_alloc_headroom(1024, 128, name);
803}
804
Harald Welteb0d95942018-07-28 23:02:48 +0200805/*! Generate a USSD ReturnResult component containing a string in default GSM alphabet.
806 * \param[in] invoke_id InvokeID of the request to which we respond
807 * \param[in] text USSD text in ASCII; to be encoded as GSM 7-but alphabet
808 */
809struct msgb *gsm0480_gen_ussd_resp_7bit(uint8_t invoke_id, const char *text)
Holger Hans Peter Freytherc64970e2010-10-18 16:56:43 +0200810{
811 struct msgb *msg;
Holger Hans Peter Freytherc64970e2010-10-18 16:56:43 +0200812 uint8_t *ptr8;
Holger Hans Peter Freyther47aa4822013-07-07 13:54:53 +0200813 int response_len;
Holger Hans Peter Freytherc64970e2010-10-18 16:56:43 +0200814
Harald Welte88fa5a32018-07-28 22:55:43 +0200815 msg = gsm0480_msgb_alloc_name("TS 04.80 USSD Resp");
Holger Hans Peter Freytherc64970e2010-10-18 16:56:43 +0200816 if (!msg)
817 return NULL;
818
819 /* First put the payload text into the message */
820 ptr8 = msgb_put(msg, 0);
Jacob Erlbeck1d7f3b52013-08-12 17:07:53 +0200821 gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), text, &response_len);
Holger Hans Peter Freyther47aa4822013-07-07 13:54:53 +0200822 msgb_put(msg, response_len);
Holger Hans Peter Freytherc64970e2010-10-18 16:56:43 +0200823
824 /* Then wrap it as an Octet String */
825 msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG);
826
827 /* Pre-pend the DCS octet string */
828 msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F);
829
830 /* Then wrap these as a Sequence */
831 msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
832
833 /* Pre-pend the operation code */
834 msgb_push_TLV1(msg, GSM0480_OPERATION_CODE,
835 GSM0480_OP_CODE_PROCESS_USS_REQ);
836
837 /* Wrap the operation code and IA5 string as a sequence */
838 msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
839
840 /* Pre-pend the invoke ID */
841 msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id);
842
843 /* Wrap this up as a Return Result component */
844 msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT);
845
Harald Welteb0d95942018-07-28 23:02:48 +0200846 return msg;
847}
848
849/*! Legacy helper: Generate USSD response including FACILITY IE + L3 header.
850 *
851 * This function is just like \ref gsm0480_gen_ussd_resp_7bit, but it generates
852 * not only the FACILITY value, but the full L3 message including message header
853 * and FACILITY IE Tag+Length.
854 */
855struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text)
856{
857 struct msgb *msg;
858
859 msg = gsm0480_gen_ussd_resp_7bit(invoke_id, text);
860 if (!msg)
861 return NULL;
862
Holger Hans Peter Freytherc64970e2010-10-18 16:56:43 +0200863 /* Wrap the component in a Facility message */
864 msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
865
866 /* And finally pre-pend the L3 header */
Vadim Yanitskiy30cfeeb2018-08-03 05:44:00 +0700867 gsm48_push_l3hdr_tid(msg, GSM48_PDISC_NC_SS,
868 /* FIXME: TI direction is always 1 ?!? */
869 trans_id | (1 << 7),
870 GSM0480_MTYPE_RELEASE_COMPLETE);
871
Holger Hans Peter Freytherc64970e2010-10-18 16:56:43 +0200872 return msg;
873}
Neels Hofmeyr25774b92016-11-26 15:21:05 +0100874
Harald Welte7ecc4a32018-07-28 23:05:36 +0200875/*! Generate a ReturnError component (see section 3.6.1) and given error code (see section 3.6.6).
876 * \param[in] invoke_id InvokeID of the request
877 * \param[in] error_code Error code (section 4.5)
878 * \return message buffer containing the Reject component
879 *
880 * Note: if InvokeID is not available, e.g. when message parsing failed, any incorrect vlue
881 * can be passed (0x00 > x > 0xff), so the universal NULL-tag (see table 3.6) will be used instead.
882 */
883struct msgb *gsm0480_gen_return_error(uint8_t invoke_id, uint8_t error_code)
884{
Vadim Yanitskiy3fc46162018-08-03 03:45:06 +0700885 struct msgb *msg;
886
887 msg = gsm0480_msgb_alloc_name("TS 04.80 ReturnError");
888 if (!msg)
889 return NULL;
Harald Welte7ecc4a32018-07-28 23:05:36 +0200890
891 /* First insert the problem code */
892 msgb_push_TLV1(msg, GSM_0480_ERROR_CODE_TAG, error_code);
893
894 /* Before it, insert the invoke ID */
895 msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id);
896
897 /* Wrap this up as a Reject component */
898 msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_ERROR);
899
900 /* FIXME: Wrap in Facility + L3? */
901 return msg;
902}
903
904/*! Generate a Reject component (see section 3.6.1) and given error code (see section 3.6.7).
905 * \param[in] invoke_id InvokeID of the request
906 * \param[in] problem_tag Problem code tag (table 3.13)
907 * \param[in] problem_code Problem code (table 3.14-3.17)
908 * \return message buffer containing the Reject component
909 *
910 * Note: if InvokeID is not available, e.g. when message parsing failed, any incorrect vlue
911 * can be passed (0x00 > x > 0xff), so the universal NULL-tag (see table 3.6) will be used instead.
912 */
913struct msgb *gsm0480_gen_reject(int invoke_id, uint8_t problem_tag, uint8_t problem_code)
914{
Vadim Yanitskiy3fc46162018-08-03 03:45:06 +0700915 struct msgb *msg;
916
917 msg = gsm0480_msgb_alloc_name("TS 04.80 Reject");
918 if (!msg)
919 return NULL;
Harald Welte7ecc4a32018-07-28 23:05:36 +0200920
921 /* First insert the problem code */
922 msgb_push_TLV1(msg, problem_tag, problem_code);
923
924 /* If the Invoke ID is not available, Universal NULL (table 3.9) with length=0 shall be used */
925 if (invoke_id < 0 || invoke_id > 255)
926 msgb_push_NULL(msg);
927 else
928 msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id);
929
930 /* Wrap this up as a Reject component */
931 msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT);
932
933 /* FIXME: Wrap in Facility + L3? */
934 return msg;
935}
936
Neels Hofmeyrbc1d7582016-11-26 15:21:15 +0100937struct msgb *gsm0480_create_ussd_notify(int level, const char *text)
938{
939 struct msgb *msg;
940
941 msg = gsm0480_create_unstructuredSS_Notify(level, text);
942 if (!msg)
943 return NULL;
944
945 gsm0480_wrap_invoke(msg, GSM0480_OP_CODE_USS_NOTIFY, 0);
946 gsm0480_wrap_facility(msg);
947
Vadim Yanitskiy30cfeeb2018-08-03 05:44:00 +0700948 /* And finally pre-pend the L3 header */
949 gsm48_push_l3hdr(msg, GSM48_PDISC_NC_SS,
950 /* FIXME: no transactionID?!? */
951 GSM0480_MTYPE_REGISTER);
952
Neels Hofmeyrbc1d7582016-11-26 15:21:15 +0100953 return msg;
954}
955
956struct msgb *gsm0480_create_ussd_release_complete(void)
957{
958 struct msgb *msg;
959
Vadim Yanitskiy39a36d02018-08-03 03:52:16 +0700960 msg = gsm0480_msgb_alloc_name("TS 04.80 USSD REL COMPL");
Neels Hofmeyrbc1d7582016-11-26 15:21:15 +0100961 if (!msg)
962 return NULL;
963
Vadim Yanitskiy30cfeeb2018-08-03 05:44:00 +0700964 /* And finally pre-pend the L3 header */
965 gsm48_push_l3hdr(msg, GSM48_PDISC_NC_SS,
966 /* FIXME: no transactionID?!? */
967 GSM0480_MTYPE_RELEASE_COMPLETE);
968
Neels Hofmeyrbc1d7582016-11-26 15:21:15 +0100969 return msg;
970}