blob: c1c99923659acbfb0fa1b78d09bf1046d63b0cbb [file] [log] [blame]
Harald Welte3b6fb082016-04-25 18:46:22 +02001/*
Harald Weltee08da972017-11-13 01:00:26 +09002 * (C) 2014 by sysmocom - s.f.m.c. GmbH
3 * Author: Jacob Erlbeck
Harald Welte3b6fb082016-04-25 18:46:22 +02004 * (C) 2015 by Holger Hans Peter Freyther
5 * (C) 2016 by Harald Welte <laforge@gnumonks.org>
6 * All Rights Reserved
7 *
Harald Weltee08da972017-11-13 01:00:26 +09008 * SPDX-License-Identifier: GPL-2.0+
Harald Welte3b6fb082016-04-25 18:46:22 +02009 *
10 * This program is free software; you can redistribute it and/or modify
Neels Hofmeyr5f460de2016-12-08 16:23:05 +010011 * 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
Harald Welte3b6fb082016-04-25 18:46:22 +020013 * (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
Neels Hofmeyr5f460de2016-12-08 16:23:05 +010018 * GNU General Public License for more details.
Harald Welte3b6fb082016-04-25 18:46:22 +020019 *
Neels Hofmeyr5f460de2016-12-08 16:23:05 +010020 * You should have received a copy of the GNU General Public License
Harald Welte3b6fb082016-04-25 18:46:22 +020021 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#include <osmocom/gsm/tlv.h>
26#include <osmocom/core/msgb.h>
27#include <osmocom/core/logging.h>
28#include <osmocom/gsm/gsm48_ie.h>
29#include <osmocom/gsm/gsup.h>
30
31#include <stdint.h>
32
Harald Welte96e2a002017-06-12 21:44:18 +020033/*! \addtogroup gsup
34 * @{
Harald Welte381a1aa2017-10-16 18:31:20 +020035 * \file gsup.c
Neels Hofmeyr87e45502017-06-20 00:17:59 +020036 * Osmocom Generic Subscriber Update Protocol
Harald Welte96e2a002017-06-12 21:44:18 +020037 */
38
Neels Hofmeyr10f5fb42017-02-09 02:09:09 +010039const struct value_string osmo_gsup_message_type_names[] = {
40 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST),
41 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR),
42 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT),
43
44 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST),
45 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR),
46 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT),
47
48 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_AUTH_FAIL_REPORT),
49
50 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_PURGE_MS_REQUEST),
51 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_PURGE_MS_ERROR),
52 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_PURGE_MS_RESULT),
53
54 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_INSERT_DATA_REQUEST),
55 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_INSERT_DATA_ERROR),
56 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_INSERT_DATA_RESULT),
57
58 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_DELETE_DATA_REQUEST),
59 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_DELETE_DATA_ERROR),
60 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_DELETE_DATA_RESULT),
61
62 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST),
63 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR),
64 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT),
Vadim Yanitskiy36c7b332018-03-31 05:23:09 +070065
66 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_PROC_SS_REQUEST),
67 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_PROC_SS_ERROR),
68 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_PROC_SS_RESULT),
69
Vadim Yanitskiyc2628312018-09-25 23:03:13 +070070 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST),
71 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_MO_FORWARD_SM_ERROR),
72 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_MO_FORWARD_SM_RESULT),
73
74 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST),
75 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_MT_FORWARD_SM_ERROR),
76 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_MT_FORWARD_SM_RESULT),
77
Vadim Yanitskiyf9ee8da2018-11-13 02:06:15 +070078 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_READY_FOR_SM_REQUEST),
79 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_READY_FOR_SM_ERROR),
80 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_READY_FOR_SM_RESULT),
81
Neels Hofmeyr10f5fb42017-02-09 02:09:09 +010082 { 0, NULL }
83};
84
Harald Welte6a6a6092018-06-16 11:10:12 +020085const struct value_string osmo_gsup_session_state_names[] = {
86 { OSMO_GSUP_SESSION_STATE_NONE, "NONE" },
87 { OSMO_GSUP_SESSION_STATE_BEGIN, "BEGIN" },
88 { OSMO_GSUP_SESSION_STATE_CONTINUE, "CONTINUE" },
89 { OSMO_GSUP_SESSION_STATE_END, "END" },
90 { 0, NULL }
91};
92
Harald Welte520ebc12018-06-11 20:27:27 +020093
94/*! return the error message type corresponding to \a type_in
95 * \returns matching error message type; -1 on error */
96int osmo_gsup_get_err_msg_type(enum osmo_gsup_message_type type_in)
97{
98 switch (type_in) {
99 case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
100 return OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR;
101 case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
102 return OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
103 case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:
104 return OSMO_GSUP_MSGT_PURGE_MS_ERROR;
105 case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
106 return OSMO_GSUP_MSGT_INSERT_DATA_ERROR;
107 case OSMO_GSUP_MSGT_DELETE_DATA_REQUEST:
108 return OSMO_GSUP_MSGT_DELETE_DATA_ERROR;
109 case OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST:
110 return OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR;
111 default:
112 return -1;
113 }
114}
115
Harald Welte3b6fb082016-04-25 18:46:22 +0200116static int decode_pdp_info(uint8_t *data, size_t data_len,
117 struct osmo_gsup_pdp_info *pdp_info)
118{
119 int rc;
120 uint8_t tag;
121 uint8_t *value;
122 size_t value_len;
123
124 /* specific parts */
125 while (data_len > 0) {
126 enum osmo_gsup_iei iei;
127
128 rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
129 if (rc < 0)
130 return -GMM_CAUSE_PROTO_ERR_UNSPEC;
131
132 iei = tag;
133
134 switch (iei) {
135 case OSMO_GSUP_PDP_CONTEXT_ID_IE:
136 pdp_info->context_id = osmo_decode_big_endian(value, value_len);
137 break;
138
139 case OSMO_GSUP_PDP_TYPE_IE:
140 pdp_info->pdp_type =
141 osmo_decode_big_endian(value, value_len) & 0x0fff;
142 break;
143
144 case OSMO_GSUP_ACCESS_POINT_NAME_IE:
145 pdp_info->apn_enc = value;
146 pdp_info->apn_enc_len = value_len;
147 break;
148
149 case OSMO_GSUP_PDP_QOS_IE:
150 pdp_info->qos_enc = value;
151 pdp_info->qos_enc_len = value_len;
152 break;
153
Holger Hans Peter Freythereb55c0d2017-07-07 16:53:30 +0200154 case OSMO_GSUP_CHARG_CHAR_IE:
155 pdp_info->pdp_charg_enc = value;
156 pdp_info->pdp_charg_enc_len = value_len;
157 break;
158
Harald Welte3b6fb082016-04-25 18:46:22 +0200159 default:
160 LOGP(DLGSUP, LOGL_ERROR,
161 "GSUP IE type %d not expected in PDP info\n", iei);
162 continue;
163 }
164 }
165
166 return 0;
167}
168
169static int decode_auth_info(uint8_t *data, size_t data_len,
170 struct osmo_auth_vector *auth_vector)
171{
172 int rc;
173 uint8_t tag;
174 uint8_t *value;
175 size_t value_len;
176 enum osmo_gsup_iei iei;
177 uint8_t presence = 0;
178
179 /* specific parts */
180 while (data_len > 0) {
181 rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
182 if (rc < 0)
183 return -GMM_CAUSE_PROTO_ERR_UNSPEC;
184
185 iei = tag;
186
187 switch (iei) {
188 case OSMO_GSUP_RAND_IE:
189 if (value_len != sizeof(auth_vector->rand))
190 goto parse_error;
191
192 memcpy(auth_vector->rand, value, value_len);
193 presence |= (1 << 0);
194 break;
195
196 case OSMO_GSUP_SRES_IE:
197 if (value_len != sizeof(auth_vector->sres))
198 goto parse_error;
199
200 memcpy(auth_vector->sres, value, value_len);
201 presence |= (1 << 1);
202 break;
203
204 case OSMO_GSUP_KC_IE:
205 if (value_len != sizeof(auth_vector->kc))
206 goto parse_error;
207
208 memcpy(auth_vector->kc, value, value_len);
209 presence |= (1 << 2);
210 break;
211
212 case OSMO_GSUP_IK_IE:
213 if (value_len != sizeof(auth_vector->ik))
214 goto parse_error;
215 memcpy(auth_vector->ik, value, value_len);
216 presence |= (1 << 4);
217 break;
218
219 case OSMO_GSUP_CK_IE:
220 if (value_len != sizeof(auth_vector->ck))
221 goto parse_error;
222 memcpy(auth_vector->ck, value, value_len);
223 presence |= (1 << 5);
224 break;
225
226 case OSMO_GSUP_AUTN_IE:
227 if (value_len != sizeof(auth_vector->autn))
228 goto parse_error;
229 memcpy(auth_vector->autn, value, value_len);
230 presence |= (1 << 6);
231 break;
232 case OSMO_GSUP_RES_IE:
233 if (value_len > sizeof(auth_vector->res))
234 goto parse_error;
235 memcpy(auth_vector->res, value, value_len);
236 auth_vector->res_len = value_len;
237 presence |= (1 << 7);
238 break;
239
240 default:
241 LOGP(DLGSUP, LOGL_ERROR,
242 "GSUP IE type %d not expected in PDP info\n", iei);
243 continue;
244 }
245 }
246
247 if (presence & 0x07)
248 auth_vector->auth_types |= OSMO_AUTH_TYPE_GSM;
249 if (presence & 0xf0)
250 auth_vector->auth_types |= OSMO_AUTH_TYPE_UMTS;
251
252 return 0;
253
254parse_error:
255 LOGP(DLGSUP, LOGL_ERROR,
256 "GSUP IE type %d, length %zu invalid in PDP info\n", iei, value_len);
257
258 return -1;
259}
260
261/*! Decode (parse) a GSUP message
262 * \param[in] const_data input data to be parsed
263 * \param[in] data_len length of input (\a const_data)
264 * \param[out] gsup_msg callee-allocated output data structure
265 * \returns 0 on success; negative otherwise
266 */
267int osmo_gsup_decode(const uint8_t *const_data, size_t data_len,
268 struct osmo_gsup_message *gsup_msg)
269{
270 int rc;
271 uint8_t tag;
272 /* the shift/match functions expect non-const pointers, but we'll
273 * either copy the data or cast pointers back to const before returning
274 * them
275 */
276 uint8_t *data = (uint8_t *)const_data;
277 uint8_t *value;
278 size_t value_len;
279 static const struct osmo_gsup_pdp_info empty_pdp_info = {0};
Neels Hofmeyr505adee2016-07-13 16:55:43 +0200280 static const struct osmo_auth_vector empty_auth_info = {{0}};
Harald Welte3b6fb082016-04-25 18:46:22 +0200281 static const struct osmo_gsup_message empty_gsup_message = {0};
282
283 *gsup_msg = empty_gsup_message;
284
285 /* generic part */
286 rc = osmo_shift_v_fixed(&data, &data_len, 1, &value);
287 if (rc < 0)
288 return -GMM_CAUSE_INV_MAND_INFO;
289
290 gsup_msg->message_type = osmo_decode_big_endian(value, 1);
291
292 rc = osmo_match_shift_tlv(&data, &data_len, OSMO_GSUP_IMSI_IE,
293 &value, &value_len);
294
295 if (rc <= 0)
296 return -GMM_CAUSE_INV_MAND_INFO;
297
298 if (value_len * 2 + 1 > sizeof(gsup_msg->imsi))
299 return -GMM_CAUSE_INV_MAND_INFO;
300
301 /* Note that gsm48_decode_bcd_number expects the number of encoded IMSI
302 * octets in the first octet. By coincidence (the TLV encoding) the byte
303 * before the value part already contains this length so we can use it
304 * here.
305 */
306 OSMO_ASSERT(value[-1] == value_len);
307 gsm48_decode_bcd_number(gsup_msg->imsi, sizeof(gsup_msg->imsi),
308 value - 1, 0);
309
310 /* specific parts */
311 while (data_len > 0) {
312 enum osmo_gsup_iei iei;
313 struct osmo_gsup_pdp_info pdp_info;
314 struct osmo_auth_vector auth_info;
315
316 rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
317 if (rc < 0)
318 return -GMM_CAUSE_PROTO_ERR_UNSPEC;
319
320 iei = tag;
321
322 switch (iei) {
323 case OSMO_GSUP_IMSI_IE:
324 case OSMO_GSUP_PDP_TYPE_IE:
325 case OSMO_GSUP_ACCESS_POINT_NAME_IE:
Harald Welte3b6fb082016-04-25 18:46:22 +0200326 case OSMO_GSUP_SRES_IE:
327 case OSMO_GSUP_KC_IE:
328 LOGP(DLGSUP, LOGL_NOTICE,
329 "GSUP IE type %d not expected (ignored)\n", iei);
330 continue;
331
332 case OSMO_GSUP_CAUSE_IE:
333 gsup_msg->cause = osmo_decode_big_endian(value, value_len);
334 break;
335
336 case OSMO_GSUP_CANCEL_TYPE_IE:
337 gsup_msg->cancel_type =
338 osmo_decode_big_endian(value, value_len) + 1;
339 break;
340
341 case OSMO_GSUP_PDP_INFO_COMPL_IE:
342 gsup_msg->pdp_info_compl = 1;
343 break;
344
345 case OSMO_GSUP_FREEZE_PTMSI_IE:
346 gsup_msg->freeze_ptmsi = 1;
347 break;
348
349 case OSMO_GSUP_PDP_CONTEXT_ID_IE:
350 /* When these IE appear in the top-level part of the
351 * message, they are used by Delete Subscr Info to delete
352 * single entries. We don't have an extra list for
353 * these but use the PDP info list instead */
354
355 /* fall through */
356
357 case OSMO_GSUP_PDP_INFO_IE:
358 if (gsup_msg->num_pdp_infos >= OSMO_GSUP_MAX_NUM_PDP_INFO) {
359 LOGP(DLGSUP, LOGL_ERROR,
360 "GSUP IE type %d (PDP_INFO) max exceeded\n",
361 iei);
362 return -GMM_CAUSE_COND_IE_ERR;
363 }
364
365 pdp_info = empty_pdp_info;
366
367 if (iei == OSMO_GSUP_PDP_INFO_IE) {
368 rc = decode_pdp_info(value, value_len, &pdp_info);
369 if (rc < 0)
370 return rc;
371 pdp_info.have_info = 1;
372 } else {
373 pdp_info.context_id =
374 osmo_decode_big_endian(value, value_len);
375 }
376
377 gsup_msg->pdp_infos[gsup_msg->num_pdp_infos++] =
378 pdp_info;
379 break;
380
381 case OSMO_GSUP_AUTH_TUPLE_IE:
382 if (gsup_msg->num_auth_vectors >= OSMO_GSUP_MAX_NUM_AUTH_INFO) {
383 LOGP(DLGSUP, LOGL_ERROR,
384 "GSUP IE type %d (AUTH_INFO) max exceeded\n",
385 iei);
386 return -GMM_CAUSE_INV_MAND_INFO;
387 }
388
389 auth_info = empty_auth_info;
390
391 rc = decode_auth_info(value, value_len, &auth_info);
392 if (rc < 0)
393 return rc;
394
395 gsup_msg->auth_vectors[gsup_msg->num_auth_vectors++] =
396 auth_info;
397 break;
398
399 case OSMO_GSUP_AUTS_IE:
Neels Hofmeyr3a5ca642017-02-21 15:53:20 +0100400 if (value_len != 14) {
Harald Welte3b6fb082016-04-25 18:46:22 +0200401 LOGP(DLGSUP, LOGL_ERROR,
Neels Hofmeyr3a5ca642017-02-21 15:53:20 +0100402 "AUTS length != 14 received\n");
Harald Welte3b6fb082016-04-25 18:46:22 +0200403 return -GMM_CAUSE_COND_IE_ERR;
404 }
405 gsup_msg->auts = value;
406 break;
407
Harald Welte766da862016-05-06 11:18:15 +0200408 case OSMO_GSUP_RAND_IE:
409 if (value_len != 16) {
410 LOGP(DLGSUP, LOGL_ERROR,
411 "RAND length != 16 received\n");
412 return -GMM_CAUSE_COND_IE_ERR;
413 }
414 gsup_msg->rand = value;
415 break;
416
Harald Welte3b6fb082016-04-25 18:46:22 +0200417 case OSMO_GSUP_MSISDN_IE:
418 gsup_msg->msisdn_enc = value;
419 gsup_msg->msisdn_enc_len = value_len;
420 break;
421
422 case OSMO_GSUP_HLR_NUMBER_IE:
423 gsup_msg->hlr_enc = value;
424 gsup_msg->hlr_enc_len = value_len;
425 break;
426
Harald Welte48dc1a52016-05-05 18:46:42 +0200427 case OSMO_GSUP_CN_DOMAIN_IE:
428 gsup_msg->cn_domain = *value;
429 break;
430
Holger Hans Peter Freythereb55c0d2017-07-07 16:53:30 +0200431 case OSMO_GSUP_CHARG_CHAR_IE:
432 gsup_msg->pdp_charg_enc = value;
433 gsup_msg->pdp_charg_enc_len = value_len;
434 break;
435
Vadim Yanitskiy72696042018-04-07 02:34:55 +0700436 case OSMO_GSUP_SESSION_ID_IE:
437 gsup_msg->session_id = osmo_decode_big_endian(value, value_len);
438 break;
439
440 case OSMO_GSUP_SESSION_STATE_IE:
441 gsup_msg->session_state = *value;
442 break;
443
Vadim Yanitskiy36c7b332018-03-31 05:23:09 +0700444 case OSMO_GSUP_SS_INFO_IE:
445 gsup_msg->ss_info = value;
446 gsup_msg->ss_info_len = value_len;
447 break;
448
Vadim Yanitskiyc2628312018-09-25 23:03:13 +0700449 case OSMO_GSUP_SM_RP_MR_IE:
450 gsup_msg->sm_rp_mr = value;
451 break;
452
453 case OSMO_GSUP_SM_RP_DA_IE:
454 rc = osmo_gsup_sms_decode_sm_rp_da(gsup_msg, value, value_len);
455 if (rc)
456 return rc;
457 break;
458
459 case OSMO_GSUP_SM_RP_OA_IE:
460 rc = osmo_gsup_sms_decode_sm_rp_oa(gsup_msg, value, value_len);
461 if (rc)
462 return rc;
463 break;
464
465 case OSMO_GSUP_SM_RP_UI_IE:
466 gsup_msg->sm_rp_ui = value;
467 gsup_msg->sm_rp_ui_len = value_len;
468 break;
469
470 case OSMO_GSUP_SM_RP_MMS_IE:
471 gsup_msg->sm_rp_mms = value;
472 break;
473
474 case OSMO_GSUP_SM_RP_CAUSE_IE:
475 gsup_msg->sm_rp_cause = value;
476 break;
477
Vadim Yanitskiyf9ee8da2018-11-13 02:06:15 +0700478 case OSMO_GSUP_SM_ALERT_RSN_IE:
479 gsup_msg->sm_alert_rsn = *value;
480 break;
481
Harald Welte3b6fb082016-04-25 18:46:22 +0200482 default:
483 LOGP(DLGSUP, LOGL_NOTICE,
484 "GSUP IE type %d unknown\n", iei);
485 continue;
486 }
487 }
488
489 return 0;
490}
491
492static void encode_pdp_info(struct msgb *msg, enum osmo_gsup_iei iei,
493 const struct osmo_gsup_pdp_info *pdp_info)
494{
495 uint8_t *len_field;
496 size_t old_len;
497 uint8_t u8;
498
499 len_field = msgb_tlv_put(msg, iei, 0, NULL) - 1;
500 old_len = msgb_length(msg);
501
502 u8 = pdp_info->context_id;
503 msgb_tlv_put(msg, OSMO_GSUP_PDP_CONTEXT_ID_IE, sizeof(u8), &u8);
504
505 if (pdp_info->pdp_type) {
506 msgb_tlv_put(msg, OSMO_GSUP_PDP_TYPE_IE,
507 OSMO_GSUP_PDP_TYPE_SIZE,
508 osmo_encode_big_endian(pdp_info->pdp_type | 0xf000,
509 OSMO_GSUP_PDP_TYPE_SIZE));
510 }
511
512 if (pdp_info->apn_enc) {
513 msgb_tlv_put(msg, OSMO_GSUP_ACCESS_POINT_NAME_IE,
514 pdp_info->apn_enc_len, pdp_info->apn_enc);
515 }
516
517 if (pdp_info->qos_enc) {
518 msgb_tlv_put(msg, OSMO_GSUP_PDP_QOS_IE,
519 pdp_info->qos_enc_len, pdp_info->qos_enc);
520 }
521
Holger Hans Peter Freythereb55c0d2017-07-07 16:53:30 +0200522 if (pdp_info->pdp_charg_enc) {
523 msgb_tlv_put(msg, OSMO_GSUP_CHARG_CHAR_IE,
524 pdp_info->pdp_charg_enc_len, pdp_info->pdp_charg_enc);
525 }
526
Harald Welte3b6fb082016-04-25 18:46:22 +0200527 /* Update length field */
528 *len_field = msgb_length(msg) - old_len;
529}
530
531static void encode_auth_info(struct msgb *msg, enum osmo_gsup_iei iei,
532 const struct osmo_auth_vector *auth_vector)
533{
534 uint8_t *len_field;
535 size_t old_len;
536
537 len_field = msgb_tlv_put(msg, iei, 0, NULL) - 1;
538 old_len = msgb_length(msg);
539
Harald Weltedb78d212016-06-06 13:47:07 +0200540 if (auth_vector->auth_types & OSMO_AUTH_TYPE_GSM) {
541 msgb_tlv_put(msg, OSMO_GSUP_RAND_IE,
542 sizeof(auth_vector->rand), auth_vector->rand);
Harald Welte3b6fb082016-04-25 18:46:22 +0200543
Harald Weltedb78d212016-06-06 13:47:07 +0200544 msgb_tlv_put(msg, OSMO_GSUP_SRES_IE,
545 sizeof(auth_vector->sres), auth_vector->sres);
Harald Welte3b6fb082016-04-25 18:46:22 +0200546
Harald Weltedb78d212016-06-06 13:47:07 +0200547 msgb_tlv_put(msg, OSMO_GSUP_KC_IE,
548 sizeof(auth_vector->kc), auth_vector->kc);
549 }
550
551 if (auth_vector->auth_types & OSMO_AUTH_TYPE_UMTS) {
552 msgb_tlv_put(msg, OSMO_GSUP_IK_IE,
553 sizeof(auth_vector->ik), auth_vector->ik);
554
555 msgb_tlv_put(msg, OSMO_GSUP_CK_IE,
556 sizeof(auth_vector->ck), auth_vector->ck);
557
558 msgb_tlv_put(msg, OSMO_GSUP_AUTN_IE,
559 sizeof(auth_vector->autn), auth_vector->autn);
560
561 msgb_tlv_put(msg, OSMO_GSUP_RES_IE,
562 auth_vector->res_len, auth_vector->res);
563 }
Harald Welte3b6fb082016-04-25 18:46:22 +0200564
565 /* Update length field */
566 *len_field = msgb_length(msg) - old_len;
567}
568
569/*! Encode a GSUP message
570 * \param[out] msg message buffer to which encoded message is written
571 * \param[in] gsup_msg \ref osmo_gsup_message data to be encoded
Max80f4c4e2018-01-24 12:33:05 +0100572 * \returns 0 on success; negative otherwise
Harald Welte3b6fb082016-04-25 18:46:22 +0200573 */
Max80f4c4e2018-01-24 12:33:05 +0100574int osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg)
Harald Welte3b6fb082016-04-25 18:46:22 +0200575{
576 uint8_t u8;
Vadim Yanitskiyc2628312018-09-25 23:03:13 +0700577 int idx, rc;
Harald Welte3b6fb082016-04-25 18:46:22 +0200578 uint8_t bcd_buf[GSM48_MI_SIZE] = {0};
579 size_t bcd_len;
580
581 /* generic part */
Max80f4c4e2018-01-24 12:33:05 +0100582 if(!gsup_msg->message_type)
Vadim Yanitskiyee86b042018-09-25 18:09:01 +0700583 return -EINVAL;
Max80f4c4e2018-01-24 12:33:05 +0100584
Harald Welte3b6fb082016-04-25 18:46:22 +0200585 msgb_v_put(msg, gsup_msg->message_type);
586
587 bcd_len = gsm48_encode_bcd_number(bcd_buf, sizeof(bcd_buf), 0,
588 gsup_msg->imsi);
589
Max80f4c4e2018-01-24 12:33:05 +0100590 if (bcd_len <= 0 || bcd_len > sizeof(bcd_buf))
591 return -EINVAL;
Harald Welte3b6fb082016-04-25 18:46:22 +0200592
593 /* Note that gsm48_encode_bcd_number puts the length into the first
594 * octet. Since msgb_tlv_put will add this length byte, we'll have to
595 * skip it */
596 msgb_tlv_put(msg, OSMO_GSUP_IMSI_IE, bcd_len - 1, &bcd_buf[1]);
597
598 /* specific parts */
599 if (gsup_msg->msisdn_enc)
600 msgb_tlv_put(msg, OSMO_GSUP_MSISDN_IE,
601 gsup_msg->msisdn_enc_len, gsup_msg->msisdn_enc);
602 if (gsup_msg->hlr_enc)
603 msgb_tlv_put(msg, OSMO_GSUP_HLR_NUMBER_IE,
604 gsup_msg->hlr_enc_len, gsup_msg->hlr_enc);
605
606 if ((u8 = gsup_msg->cause))
607 msgb_tlv_put(msg, OSMO_GSUP_CAUSE_IE, sizeof(u8), &u8);
608
609 if ((u8 = gsup_msg->cancel_type)) {
610 u8 -= 1;
611 msgb_tlv_put(msg, OSMO_GSUP_CANCEL_TYPE_IE, sizeof(u8), &u8);
612 }
613
614 if (gsup_msg->pdp_info_compl)
615 msgb_tlv_put(msg, OSMO_GSUP_PDP_INFO_COMPL_IE, 0, &u8);
616
617 if (gsup_msg->freeze_ptmsi)
618 msgb_tlv_put(msg, OSMO_GSUP_FREEZE_PTMSI_IE, 0, &u8);
619
620 for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) {
621 const struct osmo_gsup_pdp_info *pdp_info;
622
623 pdp_info = &gsup_msg->pdp_infos[idx];
624
625 if (pdp_info->context_id == 0)
626 continue;
627
628 if (pdp_info->have_info) {
629 encode_pdp_info(msg, OSMO_GSUP_PDP_INFO_IE, pdp_info);
630 } else {
631 u8 = pdp_info->context_id;
632 msgb_tlv_put(msg, OSMO_GSUP_PDP_CONTEXT_ID_IE,
633 sizeof(u8), &u8);
634 }
635 }
636
637 for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) {
638 const struct osmo_auth_vector *auth_vector;
639
640 auth_vector = &gsup_msg->auth_vectors[idx];
641
642 encode_auth_info(msg, OSMO_GSUP_AUTH_TUPLE_IE, auth_vector);
643 }
644
645 if (gsup_msg->auts)
Neels Hofmeyr8352d312017-02-02 20:05:14 +0100646 msgb_tlv_put(msg, OSMO_GSUP_AUTS_IE, 14, gsup_msg->auts);
Harald Welte48dc1a52016-05-05 18:46:42 +0200647
Harald Welte766da862016-05-06 11:18:15 +0200648 if (gsup_msg->rand)
649 msgb_tlv_put(msg, OSMO_GSUP_RAND_IE, 16, gsup_msg->rand);
650
Harald Welte48dc1a52016-05-05 18:46:42 +0200651 if (gsup_msg->cn_domain) {
652 uint8_t dn = gsup_msg->cn_domain;
653 msgb_tlv_put(msg, OSMO_GSUP_CN_DOMAIN_IE, 1, &dn);
654 }
Holger Hans Peter Freythereb55c0d2017-07-07 16:53:30 +0200655
656 if (gsup_msg->pdp_charg_enc) {
657 msgb_tlv_put(msg, OSMO_GSUP_CHARG_CHAR_IE,
658 gsup_msg->pdp_charg_enc_len, gsup_msg->pdp_charg_enc);
659 }
Max80f4c4e2018-01-24 12:33:05 +0100660
Vadim Yanitskiy72696042018-04-07 02:34:55 +0700661 if ((u8 = gsup_msg->session_state)) {
662 size_t len = sizeof(gsup_msg->session_id);
663 uint8_t *sid = osmo_encode_big_endian(gsup_msg->session_id, len);
664
665 msgb_tlv_put(msg, OSMO_GSUP_SESSION_ID_IE, len, sid);
666 msgb_tlv_put(msg, OSMO_GSUP_SESSION_STATE_IE, sizeof(u8), &u8);
667 }
668
Vadim Yanitskiy36c7b332018-03-31 05:23:09 +0700669 if (gsup_msg->ss_info) {
670 msgb_tlv_put(msg, OSMO_GSUP_SS_INFO_IE,
671 gsup_msg->ss_info_len, gsup_msg->ss_info);
672 }
673
Vadim Yanitskiyc2628312018-09-25 23:03:13 +0700674 if (gsup_msg->sm_rp_mr) {
675 msgb_tlv_put(msg, OSMO_GSUP_SM_RP_MR_IE,
676 sizeof(*gsup_msg->sm_rp_mr), gsup_msg->sm_rp_mr);
677 }
678
679 if (gsup_msg->sm_rp_da_type) {
680 rc = osmo_gsup_sms_encode_sm_rp_da(msg, gsup_msg);
681 if (rc) {
682 LOGP(DLGSUP, LOGL_ERROR, "Failed to encode SM-RP-DA IE\n");
683 return -EINVAL;
684 }
685 }
686
687 if (gsup_msg->sm_rp_oa_type) {
688 rc = osmo_gsup_sms_encode_sm_rp_oa(msg, gsup_msg);
689 if (rc) {
690 LOGP(DLGSUP, LOGL_ERROR, "Failed to encode SM-RP-OA IE\n");
691 return -EINVAL;
692 }
693 }
694
695 if (gsup_msg->sm_rp_ui) {
696 msgb_tlv_put(msg, OSMO_GSUP_SM_RP_UI_IE,
697 gsup_msg->sm_rp_ui_len, gsup_msg->sm_rp_ui);
698 }
699
700 if (gsup_msg->sm_rp_mms) {
701 msgb_tlv_put(msg, OSMO_GSUP_SM_RP_MMS_IE,
702 sizeof(*gsup_msg->sm_rp_mms), gsup_msg->sm_rp_mms);
703 }
704
705 if (gsup_msg->sm_rp_cause) {
706 msgb_tlv_put(msg, OSMO_GSUP_SM_RP_CAUSE_IE,
707 sizeof(*gsup_msg->sm_rp_cause), gsup_msg->sm_rp_cause);
708 }
709
Vadim Yanitskiyf9ee8da2018-11-13 02:06:15 +0700710 if ((u8 = gsup_msg->sm_alert_rsn)) {
711 msgb_tlv_put(msg, OSMO_GSUP_SM_ALERT_RSN_IE,
712 sizeof(u8), &u8);
713 }
714
Max80f4c4e2018-01-24 12:33:05 +0100715 return 0;
Harald Welte3b6fb082016-04-25 18:46:22 +0200716}
Harald Welte96e2a002017-06-12 21:44:18 +0200717
718/*! @} */