blob: 4b2a5f5679a3cf43a4f9a8599a95948941f1ee90 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*
Harald Weltee08da972017-11-13 01:00:26 +09002 * (C) 2016 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier
Philipp Maier22401432017-03-24 17:59:26 +01003 * All Rights Reserved
4 *
Harald Weltee08da972017-11-13 01:00:26 +09005 * SPDX-License-Identifier: GPL-2.0+
Philipp Maier22401432017-03-24 17:59:26 +01006 *
7 * This program is free software; you can redistribute it and/or modify
Harald Weltee08da972017-11-13 01:00:26 +09008 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
Philipp Maier22401432017-03-24 17:59:26 +010010 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Weltee08da972017-11-13 01:00:26 +090015 * GNU General Public License for more details.
Philipp Maier22401432017-03-24 17:59:26 +010016 *
Harald Weltee08da972017-11-13 01:00:26 +090017 * You should have received a copy of the GNU General Public License
Philipp Maier22401432017-03-24 17:59:26 +010018 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
Harald Welte20725b92017-05-15 12:50:04 +020022#include "config.h"
23
Philipp Maier22401432017-03-24 17:59:26 +010024#include <osmocom/core/utils.h>
25#include <osmocom/core/msgb.h>
Harald Welte95871da2017-05-15 12:11:36 +020026#include <osmocom/core/byteswap.h>
Philipp Maier22401432017-03-24 17:59:26 +010027#include <string.h>
Philipp Maier22401432017-03-24 17:59:26 +010028#include <errno.h>
29#include <osmocom/gsm/protocol/gsm_08_08.h>
Stefan Sperling11a4d9d2018-02-15 18:28:04 +010030#include <osmocom/gsm/gsm48.h>
31#include <osmocom/gsm/gsm0808_utils.h>
Philipp Maier22401432017-03-24 17:59:26 +010032
33#define IP_V4_ADDR_LEN 4
34#define IP_V6_ADDR_LEN 16
35#define IP_PORT_LEN 2
36
Philipp Maiere0c65302017-03-28 17:05:40 +020037#define CHANNEL_TYPE_ELEMENT_MAXLEN 11
38#define CHANNEL_TYPE_ELEMENT_MINLEN 3
Philipp Maier14e76b92017-03-28 18:36:52 +020039#define ENCRYPT_INFO_ELEMENT_MINLEN 1
Philipp Maier6f725d62017-03-24 18:03:17 +010040
Harald Welte20725b92017-05-15 12:50:04 +020041#ifdef HAVE_SYS_SOCKET_H
42
43#include <sys/socket.h>
44#include <netinet/in.h>
Harald Welte96e2a002017-06-12 21:44:18 +020045
46/*! \addtogroup gsm0808
47 * @{
Harald Welte37b61652017-10-16 18:46:03 +020048 * \file gsm0808_utils.c
Harald Welte96e2a002017-06-12 21:44:18 +020049 */
50
Neels Hofmeyr87e45502017-06-20 00:17:59 +020051/*! Encode TS 08.08 AoIP transport address IE
Harald Welte96e2a002017-06-12 21:44:18 +020052 * \param[out] msg Message Buffer to which to append IE
53 * \param[in] ss Socket Address to be used in IE
54 * \returns number of bytes added to \a msg */
Philipp Maier22401432017-03-24 17:59:26 +010055uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
56 const struct sockaddr_storage *ss)
57{
58 /* See also 3GPP TS 48.008 3.2.2.102 AoIP Transport Layer Address */
59 struct sockaddr_in *sin;
60 struct sockaddr_in6 *sin6;
61 uint16_t port = 0;
62 uint8_t *ptr;
63 uint8_t *old_tail;
64 uint8_t *tlv_len;
65
66 OSMO_ASSERT(msg);
67 OSMO_ASSERT(ss);
68 OSMO_ASSERT(ss->ss_family == AF_INET || ss->ss_family == AF_INET6);
69
70 msgb_put_u8(msg, GSM0808_IE_AOIP_TRASP_ADDR);
71 tlv_len = msgb_put(msg,1);
72 old_tail = msg->tail;
73
74 switch (ss->ss_family) {
75 case AF_INET:
76 sin = (struct sockaddr_in *)ss;
Harald Welte95871da2017-05-15 12:11:36 +020077 port = osmo_ntohs(sin->sin_port);
Philipp Maier22401432017-03-24 17:59:26 +010078 ptr = msgb_put(msg, IP_V4_ADDR_LEN);
79 memcpy(ptr, &sin->sin_addr.s_addr, IP_V4_ADDR_LEN);
80 break;
81 case AF_INET6:
82 sin6 = (struct sockaddr_in6 *)ss;
Harald Welte95871da2017-05-15 12:11:36 +020083 port = osmo_ntohs(sin6->sin6_port);
Philipp Maier22401432017-03-24 17:59:26 +010084 ptr = msgb_put(msg, IP_V6_ADDR_LEN);
85 memcpy(ptr, sin6->sin6_addr.s6_addr, IP_V6_ADDR_LEN);
86 break;
87 }
88
89 msgb_put_u16(msg, port);
90
91 *tlv_len = (uint8_t) (msg->tail - old_tail);
92 return *tlv_len + 2;
93}
94
Neels Hofmeyr87e45502017-06-20 00:17:59 +020095/*! Decode TS 08.08 AoIP transport address IE
Harald Welte96e2a002017-06-12 21:44:18 +020096 * \param[out] ss Caller-provided memory where decoded socket addr is stored
97 * \param[in] elem pointer to IE value
98 * \param[in] len length of \a elem in bytes
99 * \returns number of bytes parsed */
Philipp Maier22401432017-03-24 17:59:26 +0100100int gsm0808_dec_aoip_trasp_addr(struct sockaddr_storage *ss,
101 const uint8_t *elem, uint8_t len)
102{
103 /* See also 3GPP TS 48.008 3.2.2.102 AoIP Transport Layer Address */
104 struct sockaddr_in sin;
105 struct sockaddr_in6 sin6;
106 const uint8_t *old_elem = elem;
107
108 OSMO_ASSERT(ss);
109 if (!elem)
110 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +0200111 if (len == 0)
Philipp Maier22401432017-03-24 17:59:26 +0100112 return -EINVAL;
113
114 memset(ss, 0, sizeof(*ss));
115
116 switch (len) {
117 case IP_V4_ADDR_LEN + IP_PORT_LEN:
118 memset(&sin, 0, sizeof(sin));
119 sin.sin_family = AF_INET;
120
121 memcpy(&sin.sin_addr.s_addr, elem, IP_V4_ADDR_LEN);
122 elem += IP_V4_ADDR_LEN;
123 sin.sin_port = osmo_load16le(elem);
124 elem += IP_PORT_LEN;
125
126 memcpy(ss, &sin, sizeof(sin));
127 break;
128 case IP_V6_ADDR_LEN + IP_PORT_LEN:
129 memset(&sin6, 0, sizeof(sin6));
130 sin6.sin6_family = AF_INET6;
131
132 memcpy(sin6.sin6_addr.s6_addr, elem, IP_V6_ADDR_LEN);
133 elem += IP_V6_ADDR_LEN;
134 sin6.sin6_port = osmo_load16le(elem);
135 elem += IP_PORT_LEN;
136
137 memcpy(ss, &sin6, sizeof(sin6));
138 break;
139 default:
140 /* Malformed element! */
141 return -EINVAL;
142 break;
143 }
144
145 return (int)(elem - old_elem);
146}
Philipp Maier6f725d62017-03-24 18:03:17 +0100147
Harald Welte20725b92017-05-15 12:50:04 +0200148#endif /* HAVE_SYS_SOCKET_H */
149
Philipp Maier6f725d62017-03-24 18:03:17 +0100150/* Helper function for gsm0808_enc_speech_codec()
151 * and gsm0808_enc_speech_codec_list() */
152static uint8_t enc_speech_codec(struct msgb *msg,
153 const struct gsm0808_speech_codec *sc)
154{
155 /* See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
156 uint8_t header = 0;
157 uint8_t *old_tail;
Harald Welte459a1802018-06-28 09:24:17 +0200158 bool type_extended = false;
Philipp Maierbb839662017-06-01 17:11:19 +0200159
160 /* Note: Extended codec types are codec types that require 8 instead
161 * of 4 bit to fully specify the selected codec. In the following,
162 * we check if we work with an extended type or not. We also check
163 * if the codec type is valid at all. */
164 switch(sc->type) {
165 case GSM0808_SCT_FR1:
166 case GSM0808_SCT_FR2:
167 case GSM0808_SCT_FR3:
168 case GSM0808_SCT_FR4:
169 case GSM0808_SCT_FR5:
170 case GSM0808_SCT_HR1:
171 case GSM0808_SCT_HR3:
172 case GSM0808_SCT_HR4:
173 case GSM0808_SCT_HR6:
174 type_extended = false;
175 break;
176 case GSM0808_SCT_CSD:
177 type_extended = true;
178 break;
179 default:
180 /* Invalid codec type specified */
181 OSMO_ASSERT(false);
182 break;
183 }
Philipp Maier6f725d62017-03-24 18:03:17 +0100184
185 old_tail = msg->tail;
186
187 if (sc->fi)
188 header |= (1 << 7);
189 if (sc->pi)
190 header |= (1 << 6);
191 if (sc->pt)
192 header |= (1 << 5);
193 if (sc->tf)
194 header |= (1 << 4);
Philipp Maierbb839662017-06-01 17:11:19 +0200195
196 if (type_extended) {
Philipp Maier6f725d62017-03-24 18:03:17 +0100197 header |= 0x0f;
198 msgb_put_u8(msg, header);
Philipp Maierbb839662017-06-01 17:11:19 +0200199 msgb_put_u8(msg, sc->type);
Philipp Maier6f725d62017-03-24 18:03:17 +0100200 } else {
201 OSMO_ASSERT(sc->type < 0x0f);
202 header |= sc->type;
203 msgb_put_u8(msg, header);
Philipp Maier6f725d62017-03-24 18:03:17 +0100204 }
205
Philipp Maierbb839662017-06-01 17:11:19 +0200206 /* Note: Whether a configuration is present or not depends on the
207 * selected codec type. If present, it can either consist of one
208 * or two octets, depending on the codec type */
209 switch (sc->type) {
210 case GSM0808_SCT_FR3:
211 case GSM0808_SCT_HR3:
212 case GSM0808_SCT_HR6:
Philipp Maier7e27b142018-03-22 17:26:46 +0100213 msgb_put_u16(msg, osmo_ntohs(sc->cfg));
Philipp Maierbb839662017-06-01 17:11:19 +0200214 break;
215 case GSM0808_SCT_FR4:
216 case GSM0808_SCT_FR5:
217 case GSM0808_SCT_HR4:
218 case GSM0808_SCT_CSD:
219 OSMO_ASSERT((sc->cfg & 0xff00) == 0)
220 msgb_put_u8(msg, (uint8_t) sc->cfg & 0xff);
221 break;
222 default:
223 OSMO_ASSERT(sc->cfg == 0);
224 break;
225 }
Philipp Maier6f725d62017-03-24 18:03:17 +0100226
227 return (uint8_t) (msg->tail - old_tail);
228}
229
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200230/*! Encode TS 08.08 Speech Codec IE
Harald Welte96e2a002017-06-12 21:44:18 +0200231 * \param[out] msg Message Buffer to which IE will be appended
232 * \param[in] sc Speech Codec to be encoded into IE
233 * \returns number of bytes appended to \a msg */
Philipp Maier6f725d62017-03-24 18:03:17 +0100234uint8_t gsm0808_enc_speech_codec(struct msgb *msg,
235 const struct gsm0808_speech_codec *sc)
236{
Philipp Maier452a6bb2017-06-23 00:29:34 +0200237 /*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
Philipp Maier6f725d62017-03-24 18:03:17 +0100238 uint8_t *old_tail;
239 uint8_t *tlv_len;
240
241 OSMO_ASSERT(msg);
242 OSMO_ASSERT(sc);
243
244 msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC);
245 tlv_len = msgb_put(msg, 1);
246 old_tail = msg->tail;
247
248 enc_speech_codec(msg, sc);
249
250 *tlv_len = (uint8_t) (msg->tail - old_tail);
251 return *tlv_len + 2;
252}
253
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200254/*! Decode TS 08.08 Speech Codec IE
Harald Welte96e2a002017-06-12 21:44:18 +0200255 * \param[out] sc Caller-allocated memory for Speech Codec
256 * \param[in] elem IE value to be decoded
257 * \param[in] len Length of \a elem in bytes
258 * \returns number of bytes parsed; negative on error */
Philipp Maier6f725d62017-03-24 18:03:17 +0100259int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
260 const uint8_t *elem, uint8_t len)
261{
262 /* See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
263 uint8_t header;
264 const uint8_t *old_elem = elem;
265
266 OSMO_ASSERT(sc);
267 if (!elem)
268 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +0200269 if (len == 0)
Philipp Maier6f725d62017-03-24 18:03:17 +0100270 return -EINVAL;
271
272 memset(sc, 0, sizeof(*sc));
273
274 header = *elem;
275
Philipp Maier85a6af22017-04-28 10:55:05 +0200276 /* An extended codec type needs at least two fields,
277 * bail if the input data length is not sufficient. */
Philipp Maier6f725d62017-03-24 18:03:17 +0100278 if ((header & 0x0F) == 0x0F && len < 2)
279 return -EINVAL;
Philipp Maier6f725d62017-03-24 18:03:17 +0100280
281 elem++;
282 len--;
283
284 if (header & (1 << 7))
285 sc->fi = true;
286 if (header & (1 << 6))
287 sc->pi = true;
288 if (header & (1 << 5))
289 sc->pt = true;
290 if (header & (1 << 4))
291 sc->tf = true;
292
293 if ((header & 0x0F) != 0x0F) {
294 sc->type = (header & 0x0F);
Philipp Maierbb839662017-06-01 17:11:19 +0200295 } else {
296 sc->type = *elem;
297 elem++;
298 len--;
Philipp Maier6f725d62017-03-24 18:03:17 +0100299 }
300
Philipp Maierbb839662017-06-01 17:11:19 +0200301 /* Note: Whether a configuration is present or not depends on the
302 * selected codec type. If present, it can either consist of one or
303 * two octets depending on the codec type */
304 switch (sc->type) {
305 case GSM0808_SCT_FR1:
306 case GSM0808_SCT_FR2:
307 case GSM0808_SCT_HR1:
308 break;
309 case GSM0808_SCT_HR4:
310 case GSM0808_SCT_CSD:
311 case GSM0808_SCT_FR4:
312 case GSM0808_SCT_FR5:
313 if (len < 1)
314 return -EINVAL;
315 sc->cfg = *elem;
316 elem++;
317 break;
318 case GSM0808_SCT_FR3:
319 case GSM0808_SCT_HR3:
320 case GSM0808_SCT_HR6:
321 if (len < 2)
322 return -EINVAL;
Philipp Maier7e27b142018-03-22 17:26:46 +0100323 sc->cfg = osmo_load16le(elem);
Philipp Maierbb839662017-06-01 17:11:19 +0200324 elem += 2;
325 break;
326 default:
327 /* Invalid codec type => malformed speech codec element! */
328 return -EINVAL;
329 break;
330 }
Philipp Maier6f725d62017-03-24 18:03:17 +0100331
332 return (int)(elem - old_elem);
333}
334
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200335/*! Encode TS 08.08 Speech Codec list
Harald Welte96e2a002017-06-12 21:44:18 +0200336 * \param[out] msg Message Buffer to which IE is to be appended
337 * \param[in] scl Speech Codec List to be encoded into IE
338 * \returns number of bytes added to \a msg */
Philipp Maier6f725d62017-03-24 18:03:17 +0100339uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg,
340 const struct gsm0808_speech_codec_list *scl)
341{
Philipp Maier452a6bb2017-06-23 00:29:34 +0200342 /*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
Philipp Maier6f725d62017-03-24 18:03:17 +0100343 uint8_t *old_tail;
344 uint8_t *tlv_len;
345 unsigned int i;
346 uint8_t rc;
347 unsigned int bytes_used = 0;
348
349 OSMO_ASSERT(msg);
350 OSMO_ASSERT(scl);
351
352 /* Empty list */
353 OSMO_ASSERT(scl->len >= 1);
354
355 msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC_LIST);
356 tlv_len = msgb_put(msg, 1);
357 old_tail = msg->tail;
358
359 for (i = 0; i < scl->len; i++) {
360 rc = enc_speech_codec(msg, &scl->codec[i]);
361 OSMO_ASSERT(rc >= 1);
362 bytes_used += rc;
363 OSMO_ASSERT(bytes_used <= 255);
364 }
365
366 *tlv_len = (uint8_t) (msg->tail - old_tail);
367 return *tlv_len + 2;
368}
369
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200370/*! Decode TS 08.08 Speech Codec list IE
Harald Welte96e2a002017-06-12 21:44:18 +0200371 * \param[out] scl Caller-provided memory to store codec list
372 * \param[in] elem IE value to be decoded
373 * \param[in] len Length of \a elem in bytes
374 * \returns number of bytes parsed; negative on error */
Philipp Maier6f725d62017-03-24 18:03:17 +0100375int gsm0808_dec_speech_codec_list(struct gsm0808_speech_codec_list *scl,
376 const uint8_t *elem, uint8_t len)
377{
Philipp Maier452a6bb2017-06-23 00:29:34 +0200378 /*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
Philipp Maier6f725d62017-03-24 18:03:17 +0100379 const uint8_t *old_elem = elem;
380 unsigned int i;
381 int rc;
382 uint8_t decoded = 0;
383
384 OSMO_ASSERT(scl);
385 if (!elem)
386 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +0200387 if (len == 0)
Philipp Maier6f725d62017-03-24 18:03:17 +0100388 return -EINVAL;
389
390 memset(scl, 0, sizeof(*scl));
391
392 for (i = 0; i < ARRAY_SIZE(scl->codec); i++) {
393 if (len <= 0)
394 break;
395
396 rc = gsm0808_dec_speech_codec(&scl->codec[i], elem, len);
397 if (rc < 1)
398 return -EINVAL;
399
400 elem+=rc;
401 len -= rc;
402 decoded++;
403 }
404
405 scl->len = decoded;
406
407 /* Empty list */
408 if (decoded < 1) {
409 return -EINVAL;
410 }
411
412 return (int)(elem - old_elem);
413}
Philipp Maiere0c65302017-03-28 17:05:40 +0200414
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200415/*! Encode TS 08.08 Channel Type IE
Harald Welte96e2a002017-06-12 21:44:18 +0200416 * \param[out] msg Message Buffer to which IE is to be appended
417 * \param[in] ct Channel Type to be encoded
418 * \returns number of bytes added to \a msg */
Philipp Maiere0c65302017-03-28 17:05:40 +0200419uint8_t gsm0808_enc_channel_type(struct msgb *msg,
420 const struct gsm0808_channel_type *ct)
421{
Philipp Maier452a6bb2017-06-23 00:29:34 +0200422 /*! See also 3GPP TS 48.008 3.2.2.11 Channel Type */
Philipp Maiere0c65302017-03-28 17:05:40 +0200423 unsigned int i;
424 uint8_t byte;
425 uint8_t *old_tail;
426 uint8_t *tlv_len;
427
428 OSMO_ASSERT(msg);
429 OSMO_ASSERT(ct);
430 OSMO_ASSERT(ct->perm_spch_len <= CHANNEL_TYPE_ELEMENT_MAXLEN - 2);
431
432 /* FIXME: Implement encoding support for Data
433 * and Speech + CTM Text Telephony */
434 if ((ct->ch_indctr & 0x0f) != GSM0808_CHAN_SPEECH
435 && (ct->ch_indctr & 0x0f) != GSM0808_CHAN_SIGN)
436 OSMO_ASSERT(false);
437
438 msgb_put_u8(msg, GSM0808_IE_CHANNEL_TYPE);
439 tlv_len = msgb_put(msg, 1);
440 old_tail = msg->tail;
441
442 msgb_put_u8(msg, ct->ch_indctr & 0x0f);
443 msgb_put_u8(msg, ct->ch_rate_type);
444
445 for (i = 0; i < ct->perm_spch_len; i++) {
446 byte = ct->perm_spch[i];
447
448 if (i < ct->perm_spch_len - 1)
449 byte |= 0x80;
450 msgb_put_u8(msg, byte);
451 }
452
453 *tlv_len = (uint8_t) (msg->tail - old_tail);
454 return *tlv_len + 2;
455}
456
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200457/*! Decode TS 08.08 Channel Type IE
Harald Welte96e2a002017-06-12 21:44:18 +0200458 * \param[out] ct Caller-provided memory to store channel type
459 * \param[in] elem IE Value to be decoded
460 * \param[in] len Length of \a elem in bytes
461 * \returns number of bytes parsed; negative on error */
Philipp Maiere0c65302017-03-28 17:05:40 +0200462int gsm0808_dec_channel_type(struct gsm0808_channel_type *ct,
463 const uint8_t *elem, uint8_t len)
464{
Philipp Maier452a6bb2017-06-23 00:29:34 +0200465 /*! See also 3GPP TS 48.008 3.2.2.11 Channel Type */
Philipp Maiere0c65302017-03-28 17:05:40 +0200466 unsigned int i;
467 uint8_t byte;
468 const uint8_t *old_elem = elem;
469
470 OSMO_ASSERT(ct);
471 if (!elem)
472 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +0200473 if (len < 3 || len > 11)
Philipp Maiere0c65302017-03-28 17:05:40 +0200474 return -EINVAL;
475
476 memset(ct, 0, sizeof(*ct));
477
478 ct->ch_indctr = (*elem) & 0x0f;
479 elem++;
480 ct->ch_rate_type = (*elem) & 0x0f;
481 elem++;
482
483 for (i = 0; i < ARRAY_SIZE(ct->perm_spch); i++) {
484 byte = *elem;
485 elem++;
486 ct->perm_spch[i] = byte & 0x7f;
487 if ((byte & 0x80) == 0x00)
488 break;
489 }
490 ct->perm_spch_len = i + 1;
491
492 return (int)(elem - old_elem);
493}
Philipp Maier14e76b92017-03-28 18:36:52 +0200494
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200495/*! Encode TS 08.08 Encryption Information IE
Harald Welte96e2a002017-06-12 21:44:18 +0200496 * \param[out] msg Message Buffer to which IE is to be appended
497 * \param[in] ei Encryption Information to be encoded
498 * \returns number of bytes appended to \a msg */
Philipp Maier14e76b92017-03-28 18:36:52 +0200499uint8_t gsm0808_enc_encrypt_info(struct msgb *msg,
500 const struct gsm0808_encrypt_info *ei)
501{
502 unsigned int i;
503 uint8_t perm_algo = 0;
504 uint8_t *ptr;
505 uint8_t *old_tail;
506 uint8_t *tlv_len;
507
508 OSMO_ASSERT(msg);
509 OSMO_ASSERT(ei);
510 OSMO_ASSERT(ei->key_len <= ARRAY_SIZE(ei->key));
511 OSMO_ASSERT(ei->perm_algo_len <= ENCRY_INFO_PERM_ALGO_MAXLEN);
512
513 msgb_put_u8(msg, GSM0808_IE_ENCRYPTION_INFORMATION);
514 tlv_len = msgb_put(msg, 1);
515 old_tail = msg->tail;
516
517 for (i = 0; i < ei->perm_algo_len; i++) {
518 /* Note: gsm_08_08.h defines the permitted algorithms
519 * as an enum which ranges from 0x01 to 0x08 */
520 OSMO_ASSERT(ei->perm_algo[i] != 0);
521 OSMO_ASSERT(ei->perm_algo[i] <= ENCRY_INFO_PERM_ALGO_MAXLEN);
522 perm_algo |= (1 << (ei->perm_algo[i] - 1));
523 }
524
525 msgb_put_u8(msg, perm_algo);
526 ptr = msgb_put(msg, ei->key_len);
527 memcpy(ptr, ei->key, ei->key_len);
528
529 *tlv_len = (uint8_t) (msg->tail - old_tail);
530 return *tlv_len + 2;
531}
532
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200533/*! Decode TS 08.08 Encryption Information IE
Harald Welte96e2a002017-06-12 21:44:18 +0200534 * \param[out] ei Caller-provided memory to store encryption information
535 * \param[in] elem IE value to be decoded
536 * \param[in] len Length of \a elem in bytes
537 * \returns number of bytes parsed; negative on error */
Philipp Maier14e76b92017-03-28 18:36:52 +0200538int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
539 const uint8_t *elem, uint8_t len)
540{
541 uint8_t perm_algo;
542 unsigned int i;
543 unsigned int perm_algo_len = 0;
544 const uint8_t *old_elem = elem;
545
546 OSMO_ASSERT(ei);
547 if (!elem)
548 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +0200549 if (len == 0)
Philipp Maier14e76b92017-03-28 18:36:52 +0200550 return -EINVAL;
551
552 memset(ei, 0, sizeof(*ei));
553
554 perm_algo = *elem;
555 elem++;
556
557 for (i = 0; i < ENCRY_INFO_PERM_ALGO_MAXLEN; i++) {
558 if (perm_algo & (1 << i)) {
559 ei->perm_algo[perm_algo_len] = i + 1;
560 perm_algo_len++;
561 }
562 }
563 ei->perm_algo_len = perm_algo_len;
564
565 ei->key_len = len - 1;
566 memcpy(ei->key, elem, ei->key_len);
567 elem+=ei->key_len;
568
569 return (int)(elem - old_elem);
570}
Philipp Maier783047e2017-03-29 11:35:50 +0200571
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200572/*! Encode TS 08.08 Cell Identifier List IE
Harald Welte96e2a002017-06-12 21:44:18 +0200573 * \param[out] msg Message Buffer to which IE is to be appended
574 * \param[in] cil Cell ID List to be encoded
575 * \returns number of bytes appended to \a msg */
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100576uint8_t gsm0808_enc_cell_id_list2(struct msgb *msg,
577 const struct gsm0808_cell_id_list2 *cil)
578{
579 uint8_t *old_tail;
580 uint8_t *tlv_len;
581 unsigned int i;
582
583 OSMO_ASSERT(msg);
584 OSMO_ASSERT(cil);
585
586 msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST);
587 tlv_len = msgb_put(msg, 1);
588 old_tail = msg->tail;
589
590 msgb_put_u8(msg, cil->id_discr & 0x0f);
591
592 OSMO_ASSERT(cil->id_list_len <= GSM0808_CELL_ID_LIST2_MAXLEN)
593 switch (cil->id_discr) {
594 case CELL_IDENT_WHOLE_GLOBAL:
595 for (i = 0; i < cil->id_list_len; i++) {
596 const struct osmo_cell_global_id *id = &cil->id_list[i].global;
597 struct gsm48_loc_area_id lai;
Neels Hofmeyr8b8cd932018-03-23 01:47:37 +0100598 gsm48_generate_lai2(&lai, &id->lai);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100599 memcpy(msgb_put(msg, sizeof(lai)), &lai, sizeof(lai));
600 msgb_put_u16(msg, id->cell_identity);
601 }
602 break;
603 case CELL_IDENT_LAC_AND_CI:
604 for (i = 0; i < cil->id_list_len; i++) {
605 const struct osmo_lac_and_ci_id *id = &cil->id_list[i].lac_and_ci;
606 msgb_put_u16(msg, id->lac);
607 msgb_put_u16(msg, id->ci);
608 }
609 break;
610 case CELL_IDENT_CI:
611 for (i = 0; i < cil->id_list_len; i++)
612 msgb_put_u16(msg, cil->id_list[i].ci);
613 break;
614 case CELL_IDENT_LAI_AND_LAC:
615 for (i = 0; i < cil->id_list_len; i++) {
616 const struct osmo_location_area_id *id = &cil->id_list[i].lai_and_lac;
617 struct gsm48_loc_area_id lai;
Neels Hofmeyr8b8cd932018-03-23 01:47:37 +0100618 gsm48_generate_lai2(&lai, id);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100619 memcpy(msgb_put(msg, sizeof(lai)), &lai, sizeof(lai));
620 }
621 break;
622 case CELL_IDENT_LAC:
623 for (i = 0; i < cil->id_list_len; i++)
624 msgb_put_u16(msg, cil->id_list[i].lac);
625 break;
626 case CELL_IDENT_BSS:
627 case CELL_IDENT_NO_CELL:
628 /* Does not have any list items */
629 break;
630 default:
631 /* Support for other identifier list types is not implemented. */
632 OSMO_ASSERT(false);
633 }
634
635 *tlv_len = (uint8_t) (msg->tail - old_tail);
636 return *tlv_len + 2;
637}
638
639/*! DEPRECATED: Use gsm0808_enc_cell_id_list2 instead.
640 *
641 * Encode TS 08.08 Cell Identifier List IE
642 * \param[out] msg Message Buffer to which IE is to be appended
643 * \param[in] cil Cell ID List to be encoded
644 * \returns number of bytes appended to \a msg */
Philipp Maier783047e2017-03-29 11:35:50 +0200645uint8_t gsm0808_enc_cell_id_list(struct msgb *msg,
646 const struct gsm0808_cell_id_list *cil)
647{
648 uint8_t *old_tail;
649 uint8_t *tlv_len;
650 unsigned int i;
651
652 OSMO_ASSERT(msg);
653 OSMO_ASSERT(cil);
654
655 msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST);
656 tlv_len = msgb_put(msg, 1);
657 old_tail = msg->tail;
658
659 msgb_put_u8(msg, cil->id_discr & 0x0f);
660
661 switch (cil->id_discr) {
662 case CELL_IDENT_LAC:
663 OSMO_ASSERT(cil->id_list_len <= CELL_ID_LIST_LAC_MAXLEN)
664 for (i=0;i<cil->id_list_len;i++) {
665 msgb_put_u16(msg, cil->id_list_lac[i]);
666 }
667 break;
668 case CELL_IDENT_BSS:
669 /* Does not have any list items */
670 break;
671 default:
672 /* FIXME: Implement support for all identifier list elements */
673 OSMO_ASSERT(false);
674 }
675
676 *tlv_len = (uint8_t) (msg->tail - old_tail);
677 return *tlv_len + 2;
678}
679
Stefan Sperling23381452018-03-15 19:38:15 +0100680/* Decode 5-byte LAI list element data (see TS 08.08 3.2.2.27) into MCC/MNC/LAC. */
681static void decode_lai(const uint8_t *data, struct osmo_location_area_id *decoded)
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100682{
683 struct gsm48_loc_area_id lai;
684
Stefan Sperling23381452018-03-15 19:38:15 +0100685 /* Copy data to stack to prevent unaligned access in gsm48_decode_lai2(). */
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100686 memcpy(&lai, data, sizeof(lai)); /* don't byte swap yet */
687
Stefan Sperling23381452018-03-15 19:38:15 +0100688 gsm48_decode_lai2(&lai, decoded);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100689}
690
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100691static int parse_cell_id_global_list(struct gsm0808_cell_id_list2 *cil, const uint8_t *data, size_t remain,
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100692 size_t *consumed)
693{
694 struct osmo_cell_global_id *id;
695 uint16_t *ci_be;
696 size_t lai_offset;
697 int i = 0;
698 const size_t elemlen = sizeof(struct gsm48_loc_area_id) + sizeof(*ci_be);
699
700 *consumed = 0;
701 while (remain >= elemlen) {
702 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
703 return -ENOSPC;
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100704 id = &cil->id_list[i].global;
Stefan Sperling2873bf12018-03-14 18:38:41 +0100705 lai_offset = i * elemlen;
Stefan Sperling23381452018-03-15 19:38:15 +0100706 decode_lai(&data[lai_offset], &id->lai);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100707 ci_be = (uint16_t *)(&data[lai_offset + sizeof(struct gsm48_loc_area_id)]);
708 id->cell_identity = osmo_load16be(ci_be);
709 *consumed += elemlen;
710 remain -= elemlen;
711 i++;
712 }
713
714 return i;
715}
716
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100717static int parse_cell_id_lac_and_ci_list(struct gsm0808_cell_id_list2 *cil, const uint8_t *data, size_t remain,
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100718 size_t *consumed)
719{
720 uint16_t *lacp_be, *ci_be;
721 struct osmo_lac_and_ci_id *id;
Stefan Sperlinged4327c2018-03-16 11:02:59 +0100722 int i = 0, j = 0;
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100723 const size_t elemlen = sizeof(*lacp_be) + sizeof(*ci_be);
724
725 *consumed = 0;
726
727 if (remain < elemlen)
728 return -EINVAL;
729
Stefan Sperlinged4327c2018-03-16 11:02:59 +0100730 lacp_be = (uint16_t *)(&data[j]);
731 ci_be = (uint16_t *)(&data[j + elemlen/2]);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100732 while (remain >= elemlen) {
733 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
734 return -ENOSPC;
Stefan Sperlinged4327c2018-03-16 11:02:59 +0100735 id = &cil->id_list[i++].lac_and_ci;
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100736 id->lac = osmo_load16be(lacp_be);
737 id->ci = osmo_load16be(ci_be);
738 *consumed += elemlen;
739 remain -= elemlen;
Stefan Sperlinged4327c2018-03-16 11:02:59 +0100740 j += elemlen;
741 lacp_be = (uint16_t *)(&data[j]);
742 ci_be = (uint16_t *)(&data[j + elemlen/2]);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100743 }
744
745 return i;
746}
747
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100748static int parse_cell_id_ci_list(struct gsm0808_cell_id_list2 *cil, const uint8_t *data, size_t remain,
749 size_t *consumed)
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100750{
751 const uint16_t *ci_be = (const uint16_t *)data;
752 int i = 0;
753 const size_t elemlen = sizeof(*ci_be);
754
755 *consumed = 0;
756 while (remain >= elemlen) {
757 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
758 return -ENOSPC;
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100759 cil->id_list[i++].ci = osmo_load16be(ci_be++);
Stefan Sperling9c62fc62018-03-16 10:23:34 +0100760 *consumed += elemlen;
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100761 remain -= elemlen;
762 }
763 return i;
764}
765
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100766static int parse_cell_id_lai_and_lac(struct gsm0808_cell_id_list2 *cil, const uint8_t *data, size_t remain,
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100767 size_t *consumed)
768{
769 struct osmo_location_area_id *id;
770 int i = 0;
771 const size_t elemlen = sizeof(struct gsm48_loc_area_id);
772
773 *consumed = 0;
774 while (remain >= elemlen) {
775 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
776 return -ENOSPC;
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100777 id = &cil->id_list[i].lai_and_lac;
Stefan Sperling23381452018-03-15 19:38:15 +0100778 decode_lai(&data[i * elemlen], id);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100779 *consumed += elemlen;
780 remain -= elemlen;
781 i++;
782 }
783
784 return i;
785}
786
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100787static int parse_cell_id_lac_list(struct gsm0808_cell_id_list2 *cil, const uint8_t *data, size_t remain, size_t *consumed)
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100788{
789 const uint16_t *lac_be = (const uint16_t *)data;
790 int i = 0;
791 const size_t elemlen = sizeof(*lac_be);
792
793 *consumed = 0;
794 while (remain >= elemlen) {
795 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
796 return -ENOSPC;
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100797 cil->id_list[i++].lac = osmo_load16be(lac_be++);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100798 *consumed += elemlen;
799 remain -= elemlen;
800 }
801 return i;
802}
803
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200804/*! Decode Cell Identifier List IE
Harald Welte96e2a002017-06-12 21:44:18 +0200805 * \param[out] cil Caller-provided memory to store Cell ID list
806 * \param[in] elem IE value to be decoded
807 * \param[in] len Length of \a elem in bytes
808 * \returns number of bytes parsed; negative on error */
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100809int gsm0808_dec_cell_id_list2(struct gsm0808_cell_id_list2 *cil,
810 const uint8_t *elem, uint8_t len)
811{
812 uint8_t id_discr;
813 size_t bytes_elem = 0;
814 int list_len = 0;
815
816 OSMO_ASSERT(cil);
817 if (!elem)
818 return -EINVAL;
819 if (len == 0)
820 return -EINVAL;
821
822 memset(cil, 0, sizeof(*cil));
823
824 id_discr = *elem & 0x0f;
825 elem++;
826 len--;
827
828 switch (id_discr) {
829 case CELL_IDENT_WHOLE_GLOBAL:
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100830 list_len = parse_cell_id_global_list(cil, elem, len, &bytes_elem);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100831 break;
832 case CELL_IDENT_LAC_AND_CI:
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100833 list_len = parse_cell_id_lac_and_ci_list(cil, elem, len, &bytes_elem);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100834 break;
835 case CELL_IDENT_CI:
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100836 list_len = parse_cell_id_ci_list(cil, elem, len, &bytes_elem);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100837 break;
838 case CELL_IDENT_LAI_AND_LAC:
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100839 list_len = parse_cell_id_lai_and_lac(cil, elem, len, &bytes_elem);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100840 break;
841 case CELL_IDENT_LAC:
Stefan Sperlinge1a86742018-03-15 18:05:02 +0100842 list_len = parse_cell_id_lac_list(cil, elem, len, &bytes_elem);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100843 break;
844 case CELL_IDENT_BSS:
845 case CELL_IDENT_NO_CELL:
846 /* Does not have any list items */
847 break;
848 default:
849 /* Remaining cell identification types are not implemented. */
850 return -EINVAL;
851 }
852
853 if (list_len < 0) /* parsing error */
854 return list_len;
855
856 cil->id_discr = id_discr;
857 cil->id_list_len = list_len;
858
859 /* One byte for the cell ID discriminator + any remaining bytes in
860 * the IE which were consumed by the parser functions above. */
861 return 1 + (int)bytes_elem;
862}
863
864/*! DEPRECATED: Use gsm0808_dec_cell_id_list2 instead.
865 *
866 * Decode Cell Identifier List IE
867 * \param[out] cil Caller-provided memory to store Cell ID list
868 * \param[in] elem IE value to be decoded
869 * \param[in] len Length of \a elem in bytes
870 * \returns number of bytes parsed; negative on error */
Philipp Maier783047e2017-03-29 11:35:50 +0200871int gsm0808_dec_cell_id_list(struct gsm0808_cell_id_list *cil,
872 const uint8_t *elem, uint8_t len)
873{
874 uint8_t id_discr;
875 const uint8_t *old_elem = elem;
876 unsigned int item_count = 0;
877
878 OSMO_ASSERT(cil);
879 if (!elem)
880 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +0200881 if (len == 0)
Philipp Maier783047e2017-03-29 11:35:50 +0200882 return -EINVAL;
883
884 memset(cil, 0, sizeof(*cil));
885
886 id_discr = *elem & 0x0f;
887 elem++;
888 len--;
889
890 cil->id_discr = id_discr;
891
892 switch (id_discr) {
893 case CELL_IDENT_LAC:
894 while (len >= 2) {
895 cil->id_list_lac[item_count] = osmo_load16be(elem);
896 elem += 2;
897 item_count++;
898 len -= 2;
899 }
900 case CELL_IDENT_BSS:
901 /* Does not have any list items */
902 break;
903 default:
904 /* FIXME: Implement support for all identifier list elements */
905 return -EINVAL;
906 }
907
908 cil->id_list_len = item_count;
909 return (int)(elem - old_elem);
910}
Harald Welte96e2a002017-06-12 21:44:18 +0200911
Neels Hofmeyr74663d92018-03-23 01:46:42 +0100912static bool same_cell_id_list_entries(const struct gsm0808_cell_id_list2 *a, int ai,
913 const struct gsm0808_cell_id_list2 *b, int bi)
914{
915 struct gsm0808_cell_id_list2 tmp = {
916 .id_discr = a->id_discr,
917 .id_list_len = 1,
918 };
919 uint8_t buf_a[32 + sizeof(struct msgb)];
920 uint8_t buf_b[32 + sizeof(struct msgb)];
921 struct msgb *msg_a = (void*)buf_a;
922 struct msgb *msg_b = (void*)buf_b;
923
924 msg_a->data_len = 32;
925 msg_b->data_len = 32;
926 msgb_reset(msg_a);
927 msgb_reset(msg_b);
928
929 if (a->id_discr != b->id_discr)
930 return false;
931 if (ai >= a->id_list_len
932 || bi >= b->id_list_len)
933 return false;
934
935 tmp.id_list[0] = a->id_list[ai];
936 gsm0808_enc_cell_id_list2(msg_a, &tmp);
937
938 tmp.id_list[0] = b->id_list[bi];
939 gsm0808_enc_cell_id_list2(msg_b, &tmp);
940
941 if (msg_a->len != msg_b->len)
942 return false;
943 if (memcmp(msg_a->data, msg_b->data, msg_a->len))
944 return false;
945
946 return true;
947}
948
949/*! Append entries from one Cell Identifier List to another.
950 * The cell identifier types must be identical between the two lists.
951 * \param dst[out] Append entries to this list.
952 * \param src[in] Append these entries to \a dst.
953 * \returns the nr of items added, or negative on error: -EINVAL if the id_discr mismatch
954 * between the lists, -ENOSPC if the destination list does not have enough space. If an error is
955 * returned, \a dst may have already been changed (particularly on -ENOSPC). Note that a return value
956 * of zero may occur when the src->id_list_len is zero, or when all entries from \a src already exist
957 * in \a dst, and does not indicate error per se. */
958int gsm0808_cell_id_list_add(struct gsm0808_cell_id_list2 *dst, const struct gsm0808_cell_id_list2 *src)
959{
960 int i, j;
961 int added = 0;
962
963 if (dst->id_list_len == 0
964 && dst->id_discr != CELL_IDENT_BSS)
965 dst->id_discr = src->id_discr;
966 else if (dst->id_discr != src->id_discr)
967 return -EINVAL;
968
969 for (i = 0; i < src->id_list_len; i++) {
970 /* don't add duplicate entries */
971 bool skip = false;
972 for (j = 0; j < dst->id_list_len; j++) {
973 if (same_cell_id_list_entries(dst, j, src, i)) {
974 skip = true;
975 break;
976 }
977 }
978 if (skip)
979 continue;
980
981 if (dst->id_list_len >= ARRAY_SIZE(dst->id_list))
982 return -ENOSPC;
983
984 dst->id_list[dst->id_list_len++] = src->id_list[i];
985 added ++;
986 }
987
988 return added;
989}
990
Neels Hofmeyr38e58412018-05-25 16:56:35 +0200991/*! Convert a single Cell Identifier to a Cell Identifier List with one entry.
992 * \param dst[out] Overwrite this list.
993 * \param src[in] Set \a dst to contain exactly this item.
994 */
995void gsm0808_cell_id_to_list(struct gsm0808_cell_id_list2 *dst, const struct gsm0808_cell_id *src)
996{
997 if (!dst)
998 return;
999 if (!src) {
1000 *dst = (struct gsm0808_cell_id_list2){
1001 .id_discr = CELL_IDENT_NO_CELL,
1002 };
1003 return;
1004 }
1005
1006 *dst = (struct gsm0808_cell_id_list2){
1007 .id_discr = src->id_discr,
1008 .id_list = { src->id },
1009 .id_list_len = 1,
1010 };
1011
1012 switch (src->id_discr) {
1013 case CELL_IDENT_NO_CELL:
1014 case CELL_IDENT_BSS:
1015 dst->id_list_len = 0;
1016 break;
1017 default:
1018 break;
1019 }
1020}
1021
Neels Hofmeyr250e7f72018-04-13 03:30:14 +02001022/*! Encode Cell Identifier IE (3GPP TS 48.008 3.2.2.17).
1023 * \param[out] msg Message Buffer to which IE is to be appended
1024 * \param[in] ci Cell ID to be encoded
1025 * \returns number of bytes appended to \a msg */
1026uint8_t gsm0808_enc_cell_id(struct msgb *msg, const struct gsm0808_cell_id *ci)
1027{
1028 uint8_t rc;
1029 uint8_t *ie_tag;
1030 struct gsm0808_cell_id_list2 cil = {
1031 .id_discr = ci->id_discr,
1032 .id_list = { ci->id },
1033 .id_list_len = 1,
1034 };
1035
1036 OSMO_ASSERT(msg);
1037 OSMO_ASSERT(ci);
1038
1039 ie_tag = msg->tail;
1040 rc = gsm0808_enc_cell_id_list2(msg, &cil);
1041
1042 if (rc <= 0)
1043 return rc;
1044
1045 *ie_tag = GSM0808_IE_CELL_IDENTIFIER;
1046 return rc;
1047}
1048
1049/*! Decode Cell Identifier IE (3GPP TS 48.008 3.2.2.17).
1050 * \param[out] ci Caller-provided memory to store Cell ID.
1051 * \param[in] elem IE value to be decoded.
1052 * \param[in] len Length of \a elem in bytes.
1053 * \returns number of bytes parsed; negative on error */
1054int gsm0808_dec_cell_id(struct gsm0808_cell_id *ci, const uint8_t *elem, uint8_t len)
1055{
1056 struct gsm0808_cell_id_list2 cil;
1057 int rc;
1058 rc = gsm0808_dec_cell_id_list2(&cil, elem, len);
1059 if (rc < 0)
1060 return rc;
1061 if (cil.id_discr == CELL_IDENT_BSS || cil.id_discr == CELL_IDENT_NO_CELL) {
1062 if (cil.id_list_len != 0)
1063 return -EINVAL;
1064 } else {
1065 if (cil.id_list_len != 1)
1066 return -EINVAL;
1067 }
1068 ci->id_discr = cil.id_discr;
1069 ci->id = cil.id_list[0];
1070 return rc;
1071}
1072
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001073/*! Convert the representation of the permitted speech codec identifier
Philipp Maier3149b0d2017-06-02 13:22:34 +02001074 * that is used in struct gsm0808_channel_type to the speech codec
1075 * representation we use in struct gsm0808_speech_codec.
1076 * \param[in] perm_spch to be converted (see also gsm0808_permitted_speech)
1077 * \returns GSM speech codec type; negative on error */
1078int gsm0808_chan_type_to_speech_codec(uint8_t perm_spch)
1079{
1080 /*! The speech codec type, which is used in the channel type field to
1081 * signal the permitted speech versions (codecs) has a different
1082 * encoding than the type field in the speech codec type element
1083 * (See also 3GPP TS 48.008, 3.2.2.11 and 3.2.2.103) */
1084
1085 switch (perm_spch) {
1086 case GSM0808_PERM_FR1:
1087 return GSM0808_SCT_FR1;
1088 case GSM0808_PERM_FR2:
1089 return GSM0808_SCT_FR2;
1090 case GSM0808_PERM_FR3:
1091 return GSM0808_SCT_FR3;
1092 case GSM0808_PERM_FR4:
1093 return GSM0808_SCT_FR4;
1094 case GSM0808_PERM_FR5:
1095 return GSM0808_SCT_FR5;
1096 case GSM0808_PERM_HR1:
1097 return GSM0808_SCT_HR1;
1098 case GSM0808_PERM_HR3:
1099 return GSM0808_SCT_HR3;
1100 case GSM0808_PERM_HR4:
1101 return GSM0808_SCT_HR4;
1102 case GSM0808_PERM_HR6:
1103 return GSM0808_SCT_HR6;
1104 }
1105
1106 /* Invalid input */
1107 return -EINVAL;
1108}
1109
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001110/*! Extrapolate a speech codec field from a given permitted speech
Philipp Maier884ba0f2017-06-02 13:49:16 +02001111 * parameter (channel type).
1112 * \param[out] sc Caller provided memory to store the resulting speech codec
1113 * \param[in] perm_spch value that is used to derive the speech codec info
1114 * (see also: enum gsm0808_speech_codec_type in gsm0808_utils.h)
1115 * \returns zero when successful; negative on error */
1116int gsm0808_speech_codec_from_chan_type(struct gsm0808_speech_codec *sc,
1117 uint8_t perm_spch)
1118{
1119 int rc;
1120
1121 memset(sc, 0, sizeof(*sc));
1122
1123 /* Determine codec type */
1124 rc = gsm0808_chan_type_to_speech_codec(perm_spch);
1125 if (rc < 0)
1126 return -EINVAL;
1127 sc->type = (uint8_t) rc;
1128
1129 /* Depending on the speech codec type, pick a default codec
1130 * configuration that exactly matches the configuration on the
1131 * air interface. */
1132 switch (sc->type) {
1133 case GSM0808_SCT_FR3:
1134 sc->cfg = GSM0808_SC_CFG_DEFAULT_FR_AMR;
1135 break;
1136 case GSM0808_SCT_FR4:
1137 sc->cfg = GSM0808_SC_CFG_DEFAULT_OFR_AMR_WB;
1138 break;
1139 case GSM0808_SCT_FR5:
1140 sc->cfg = GSM0808_SC_CFG_DEFAULT_FR_AMR_WB;
1141 break;
1142 case GSM0808_SCT_HR3:
1143 sc->cfg = GSM0808_SC_CFG_DEFAULT_HR_AMR;
1144 break;
1145 case GSM0808_SCT_HR4:
1146 sc->cfg = GSM0808_SC_CFG_DEFAULT_OHR_AMR_WB;
1147 break;
1148 case GSM0808_SCT_HR6:
1149 sc->cfg = GSM0808_SC_CFG_DEFAULT_OHR_AMR;
1150 break;
1151 default:
1152 /* Note: Not all codec types specify a default setting,
1153 * in this case, we just set the field to zero. */
1154 sc->cfg = 0;
1155 }
1156
1157 /* Tag all codecs as "Full IP"
1158 * (see als 3GPP TS 48.008 3.2.2.103) */
1159 sc->fi = true;
1160
1161 return 0;
1162}
1163
Philipp Maier5f2eb152018-09-19 13:40:21 +02001164/*! Determine a set of AMR speech codec configuration bits (S0-S15) from a
1165 * given GSM 04.08 AMR configuration struct.
1166 * \param[in] cfg AMR configuration in GSM 04.08 format.
1167 * \param[in] hint if the resulting configuration shall be used with a FR or HR TCH.
1168 * \returns configuration bits (S0-S15) */
1169uint16_t gsm0808_sc_cfg_from_gsm48_mr_cfg(struct gsm48_multi_rate_conf *cfg,
1170 bool fr)
1171{
1172 uint16_t s15_s0 = 0;
1173
1174 /* Check each rate bit in the AMR multirate configuration and pick the
1175 * matching default configuration as specified in 3GPP TS 28.062,
1176 * Table 7.11.3.1.3-2. */
1177 if (cfg->m4_75)
1178 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_4_75;
1179 if (cfg->m5_15)
1180 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_5_15;
1181 if (cfg->m5_90)
1182 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_5_90;
1183 if (cfg->m6_70)
1184 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_6_70;
1185 if (cfg->m7_40)
1186 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_7_40;
1187 if (cfg->m7_95)
1188 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_7_95;
1189 if (cfg->m10_2)
1190 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_10_2;
1191 if (cfg->m12_2)
1192 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_12_2;
1193
1194 /* Note: 3GPP TS 48.008, chapter 3GPP TS 48.008 states that for AMR
1195 * some of the configuration bits must be coded as zeros. The applied
1196 * bitmask matches the default codec settings. See also the definition
1197 * of enum gsm0808_speech_codec_defaults in gsm_08_08.h and
1198 * 3GPP TS 28.062, Table 7.11.3.1.3-2. */
1199 if (fr)
1200 s15_s0 &= GSM0808_SC_CFG_DEFAULT_FR_AMR;
1201 else
1202 s15_s0 &= GSM0808_SC_CFG_DEFAULT_HR_AMR;
1203
1204 return s15_s0;
1205}
1206
Neels Hofmeyra4399c82018-04-17 02:26:10 +02001207/*! Print a human readable name of the cell identifier to the char buffer.
1208 * This is useful both for struct gsm0808_cell_id and struct gsm0808_cell_id_list2.
1209 * See also gsm0808_cell_id_name() and gsm0808_cell_id_list_name().
1210 * \param[out] buf Destination buffer to write string representation to.
1211 * \param[in] buflen Amount of memory available in \a buf.
1212 * \param[in] id_discr Cell Identifier type.
1213 * \param[in] u Cell Identifer value.
1214 * \returns Like snprintf(): the amount of characters (excluding terminating nul) written,
1215 * or that would have been written if the buffer were large enough.
1216 */
1217int gsm0808_cell_id_u_name(char *buf, size_t buflen,
1218 enum CELL_IDENT id_discr, const union gsm0808_cell_id_u *u)
1219{
1220 switch (id_discr) {
1221 case CELL_IDENT_LAC:
1222 return snprintf(buf, buflen, "%u", u->lac);
1223 case CELL_IDENT_CI:
1224 return snprintf(buf, buflen, "%u", u->ci);
1225 case CELL_IDENT_LAC_AND_CI:
1226 return snprintf(buf, buflen, "%u-%u", u->lac_and_ci.lac, u->lac_and_ci.ci);
1227 case CELL_IDENT_LAI_AND_LAC:
1228 return snprintf(buf, buflen, "%s", osmo_lai_name(&u->lai_and_lac));
1229 case CELL_IDENT_WHOLE_GLOBAL:
1230 return snprintf(buf, buflen, "%s", osmo_cgi_name(&u->global));
1231 default:
1232 /* For CELL_IDENT_BSS and CELL_IDENT_NO_CELL, just print the discriminator.
1233 * Same for kinds we have no string representation of yet. */
1234 return snprintf(buf, buflen, "%s", gsm0808_cell_id_discr_name(id_discr));
1235 }
1236}
1237
1238/*! value_string[] for enum CELL_IDENT. */
1239const struct value_string gsm0808_cell_id_discr_names[] = {
1240 { CELL_IDENT_WHOLE_GLOBAL, "CGI" },
1241 { CELL_IDENT_LAC_AND_CI, "LAC-CI" },
1242 { CELL_IDENT_CI, "CI" },
1243 { CELL_IDENT_NO_CELL, "NO-CELL" },
1244 { CELL_IDENT_LAI_AND_LAC, "LAI" },
1245 { CELL_IDENT_LAC, "LAC" },
1246 { CELL_IDENT_BSS, "BSS" },
1247 { CELL_IDENT_UTRAN_PLMN_LAC_RNC, "UTRAN-PLMN-LAC-RNC" },
1248 { CELL_IDENT_UTRAN_RNC, "UTRAN-RNC" },
1249 { CELL_IDENT_UTRAN_LAC_RNC, "UTRAN-LAC-RNC" },
1250 { 0, NULL }
1251};
1252
1253#define APPEND_THING(func, args...) do { \
1254 int remain = buflen - (pos - buf); \
1255 int l = func(pos, remain, ##args); \
1256 if (l < 0 || l > remain) \
1257 pos = buf + buflen; \
1258 else \
1259 pos += l; \
1260 if (l > 0) \
1261 total_len += l; \
1262 } while(0)
1263#define APPEND_STR(fmt, args...) APPEND_THING(snprintf, fmt, ##args)
1264#define APPEND_CELL_ID_U(DISCR, U) APPEND_THING(gsm0808_cell_id_u_name, DISCR, U)
1265
1266static const char *gsm0808_cell_id_name_buf(const struct gsm0808_cell_id *cid,
1267 char *buf, size_t buflen)
1268{
1269 char *pos = buf;
1270 int total_len = 0;
1271 APPEND_STR("%s:", gsm0808_cell_id_discr_name(cid->id_discr));
1272 APPEND_CELL_ID_U(cid->id_discr, &cid->id);
1273 return buf;
1274}
1275
1276/*! Return a human readable representation of a Cell Identifier, like "LAC:123"
1277 * or "CGI:001-01-42-23".
1278 * \param[in] cid Cell Identifer.
1279 * \returns String in a static buffer.
1280 */
1281const char *gsm0808_cell_id_name(const struct gsm0808_cell_id *cid)
1282{
1283 static char buf[64];
1284 return gsm0808_cell_id_name_buf(cid, buf, sizeof(buf));
1285}
1286
1287/*! Like gsm0808_cell_id_name() but uses a different static buffer.
1288 * \param[in] cid Cell Identifer.
1289 * \returns String in a static buffer.
1290 */
1291const char *gsm0808_cell_id_name2(const struct gsm0808_cell_id *cid)
1292{
1293 static char buf[64];
1294 return gsm0808_cell_id_name_buf(cid, buf, sizeof(buf));
1295}
1296
1297/*! Return a human readable representation of the Cell Identifier List, like
1298 * "LAC[2]:{123, 456}".
1299 * The return value semantics are like snprintf() and thus allow ensuring a complete
1300 * untruncated string by determining the required string length from the return value.
1301 * If buflen > 0, always nul-terminate the string in buf, also when it is truncated.
1302 * If buflen == 0, do not modify buf, just return the would-be length.
1303 * \param[out] buf Destination buffer to write string representation to.
1304 * \param[in] buflen Amount of memory available in \a buf.
1305 * \param[in] cil Cell Identifer List.
1306 * \returns Like snprintf(): the amount of characters (excluding terminating nul) written,
1307 * or that would have been written if the buffer were large enough.
1308 */
1309int gsm0808_cell_id_list_name_buf(char *buf, size_t buflen, const struct gsm0808_cell_id_list2 *cil)
1310{
1311 char *pos = buf;
1312 int total_len = 0;
1313 int i;
1314
1315 APPEND_STR("%s[%u]", gsm0808_cell_id_discr_name(cil->id_discr), cil->id_list_len);
1316
1317 switch (cil->id_discr) {
1318 case CELL_IDENT_BSS:
1319 case CELL_IDENT_NO_CELL:
1320 return total_len;
1321 default:
1322 break;
1323 }
1324
1325 APPEND_STR(":{");
1326
1327 for (i = 0; i < cil->id_list_len; i++) {
1328 if (i)
1329 APPEND_STR(", ");
1330 APPEND_CELL_ID_U(cil->id_discr, &cil->id_list[i]);
1331 }
1332
1333 APPEND_STR("}");
1334 return total_len;
1335}
1336
1337/*! Return a human-readable representation of \a cil in a static buffer.
1338 * If the list is too long, the output may be truncated.
1339 * See also gsm0808_cell_id_list_name_buf(). */
1340const char *gsm0808_cell_id_list_name(const struct gsm0808_cell_id_list2 *cil)
1341{
1342 static char buf[1024];
1343 gsm0808_cell_id_list_name_buf(buf, sizeof(buf), cil);
1344 return buf;
1345}
1346
1347#undef APPEND_STR
1348#undef APPEND_CELL_ID_U
1349
Neels Hofmeyrafacc2b2018-04-16 22:41:51 +02001350const char *gsm0808_channel_type_name(const struct gsm0808_channel_type *ct)
1351{
1352 static char buf[128];
1353 snprintf(buf, sizeof(buf), "ch_indctr=0x%x ch_rate_type=0x%x perm_spch=%s",
1354 ct->ch_indctr, ct->ch_rate_type,
1355 osmo_hexdump(ct->perm_spch, ct->perm_spch_len));
1356 return buf;
1357}
1358
Harald Welte96e2a002017-06-12 21:44:18 +02001359/*! @} */