blob: 040872b86a706e37a5f4202d04141b90fa3525ea [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>
Max5ec0cf52019-01-15 16:37:09 +010031#include <osmocom/gsm/gsm0808.h>
Stefan Sperling11a4d9d2018-02-15 18:28:04 +010032#include <osmocom/gsm/gsm0808_utils.h>
Philipp Maier22401432017-03-24 17:59:26 +010033
34#define IP_V4_ADDR_LEN 4
35#define IP_V6_ADDR_LEN 16
36#define IP_PORT_LEN 2
37
Philipp Maiere0c65302017-03-28 17:05:40 +020038#define CHANNEL_TYPE_ELEMENT_MAXLEN 11
39#define CHANNEL_TYPE_ELEMENT_MINLEN 3
Philipp Maier14e76b92017-03-28 18:36:52 +020040#define ENCRYPT_INFO_ELEMENT_MINLEN 1
Philipp Maier6f725d62017-03-24 18:03:17 +010041
Harald Welte20725b92017-05-15 12:50:04 +020042#ifdef HAVE_SYS_SOCKET_H
43
44#include <sys/socket.h>
45#include <netinet/in.h>
Harald Welte96e2a002017-06-12 21:44:18 +020046
47/*! \addtogroup gsm0808
48 * @{
Harald Welte37b61652017-10-16 18:46:03 +020049 * \file gsm0808_utils.c
Harald Welte96e2a002017-06-12 21:44:18 +020050 */
51
Philipp Maier4f4905f2018-11-30 13:36:12 +010052/*! Encode TS 08.08 AoIP Cause IE
53 * \param[out] msg Message Buffer to which to append IE
54 * \param[in] cause Cause code to be used in IE
55 * \returns number of bytes added to \a msg */
56uint8_t gsm0808_enc_cause(struct msgb *msg, uint16_t cause)
57{
58 /* See also 3GPP TS 48.008 3.2.2.5 Cause */
59 uint8_t *old_tail;
60 bool extended;
61
62 old_tail = msg->tail;
63
64 extended = gsm0808_cause_ext(cause >> 8);
65
66 msgb_put_u8(msg, GSM0808_IE_CAUSE);
67 if (extended) {
68 msgb_put_u8(msg, 2);
69 msgb_put_u16(msg, cause);
70 } else {
71 msgb_put_u8(msg, 1);
72 msgb_put_u8(msg, (uint8_t) (cause & 0xFF));
73 }
74
75 return (uint8_t) (msg->tail - old_tail);
76}
77
Neels Hofmeyr87e45502017-06-20 00:17:59 +020078/*! Encode TS 08.08 AoIP transport address IE
Harald Welte96e2a002017-06-12 21:44:18 +020079 * \param[out] msg Message Buffer to which to append IE
80 * \param[in] ss Socket Address to be used in IE
81 * \returns number of bytes added to \a msg */
Philipp Maier22401432017-03-24 17:59:26 +010082uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
83 const struct sockaddr_storage *ss)
84{
85 /* See also 3GPP TS 48.008 3.2.2.102 AoIP Transport Layer Address */
86 struct sockaddr_in *sin;
87 struct sockaddr_in6 *sin6;
88 uint16_t port = 0;
89 uint8_t *ptr;
90 uint8_t *old_tail;
91 uint8_t *tlv_len;
92
93 OSMO_ASSERT(msg);
94 OSMO_ASSERT(ss);
95 OSMO_ASSERT(ss->ss_family == AF_INET || ss->ss_family == AF_INET6);
96
97 msgb_put_u8(msg, GSM0808_IE_AOIP_TRASP_ADDR);
98 tlv_len = msgb_put(msg,1);
99 old_tail = msg->tail;
100
101 switch (ss->ss_family) {
102 case AF_INET:
103 sin = (struct sockaddr_in *)ss;
Harald Welte95871da2017-05-15 12:11:36 +0200104 port = osmo_ntohs(sin->sin_port);
Philipp Maier22401432017-03-24 17:59:26 +0100105 ptr = msgb_put(msg, IP_V4_ADDR_LEN);
106 memcpy(ptr, &sin->sin_addr.s_addr, IP_V4_ADDR_LEN);
107 break;
108 case AF_INET6:
109 sin6 = (struct sockaddr_in6 *)ss;
Harald Welte95871da2017-05-15 12:11:36 +0200110 port = osmo_ntohs(sin6->sin6_port);
Philipp Maier22401432017-03-24 17:59:26 +0100111 ptr = msgb_put(msg, IP_V6_ADDR_LEN);
112 memcpy(ptr, sin6->sin6_addr.s6_addr, IP_V6_ADDR_LEN);
113 break;
114 }
115
116 msgb_put_u16(msg, port);
117
118 *tlv_len = (uint8_t) (msg->tail - old_tail);
119 return *tlv_len + 2;
120}
121
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200122/*! Decode TS 08.08 AoIP transport address IE
Harald Welte96e2a002017-06-12 21:44:18 +0200123 * \param[out] ss Caller-provided memory where decoded socket addr is stored
124 * \param[in] elem pointer to IE value
125 * \param[in] len length of \a elem in bytes
126 * \returns number of bytes parsed */
Philipp Maier22401432017-03-24 17:59:26 +0100127int gsm0808_dec_aoip_trasp_addr(struct sockaddr_storage *ss,
128 const uint8_t *elem, uint8_t len)
129{
130 /* See also 3GPP TS 48.008 3.2.2.102 AoIP Transport Layer Address */
131 struct sockaddr_in sin;
132 struct sockaddr_in6 sin6;
133 const uint8_t *old_elem = elem;
134
135 OSMO_ASSERT(ss);
136 if (!elem)
137 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +0200138 if (len == 0)
Philipp Maier22401432017-03-24 17:59:26 +0100139 return -EINVAL;
140
141 memset(ss, 0, sizeof(*ss));
142
143 switch (len) {
144 case IP_V4_ADDR_LEN + IP_PORT_LEN:
145 memset(&sin, 0, sizeof(sin));
146 sin.sin_family = AF_INET;
147
148 memcpy(&sin.sin_addr.s_addr, elem, IP_V4_ADDR_LEN);
149 elem += IP_V4_ADDR_LEN;
150 sin.sin_port = osmo_load16le(elem);
151 elem += IP_PORT_LEN;
152
153 memcpy(ss, &sin, sizeof(sin));
154 break;
155 case IP_V6_ADDR_LEN + IP_PORT_LEN:
156 memset(&sin6, 0, sizeof(sin6));
157 sin6.sin6_family = AF_INET6;
158
159 memcpy(sin6.sin6_addr.s6_addr, elem, IP_V6_ADDR_LEN);
160 elem += IP_V6_ADDR_LEN;
161 sin6.sin6_port = osmo_load16le(elem);
162 elem += IP_PORT_LEN;
163
164 memcpy(ss, &sin6, sizeof(sin6));
165 break;
166 default:
167 /* Malformed element! */
168 return -EINVAL;
169 break;
170 }
171
172 return (int)(elem - old_elem);
173}
Philipp Maier6f725d62017-03-24 18:03:17 +0100174
Pau Espin Pedrol18506c82019-04-16 15:47:59 +0200175/*! Decode TS 08.08 (Osmocom Extension) Osmux CID
176 * TV with len(V) == 1, and V is the CID to be used.
177 * \param[out] cid Caller-provided variable where CID is stored
178 * \param[in] elem pointer to IE value
179 * \param[in] len length of \a elem in bytes
180 * \returns number of bytes parsed */
181int gsm0808_dec_osmux_cid(uint8_t *cid, const uint8_t *elem, uint8_t len)
182{
183 OSMO_ASSERT(cid);
184 if (!elem)
185 return -EINVAL;
186 if (len != 1)
187 return -EINVAL;
188
189 *cid = *elem;
190
191 return 1;
192}
193
Harald Welte20725b92017-05-15 12:50:04 +0200194#endif /* HAVE_SYS_SOCKET_H */
195
Harald Welteef7be492019-05-03 21:01:13 +0200196/* Decode 5-byte LAI list element data (see TS 08.08 3.2.2.27) into MCC/MNC/LAC. */
197static void decode_lai(const uint8_t *data, struct osmo_location_area_id *decoded)
198{
199 struct gsm48_loc_area_id lai;
200
201 /* Copy data to stack to prevent unaligned access in gsm48_decode_lai2(). */
202 memcpy(&lai, data, sizeof(lai)); /* don't byte swap yet */
203
204 gsm48_decode_lai2(&lai, decoded);
205}
206
Philipp Maier6f725d62017-03-24 18:03:17 +0100207/* Helper function for gsm0808_enc_speech_codec()
208 * and gsm0808_enc_speech_codec_list() */
209static uint8_t enc_speech_codec(struct msgb *msg,
210 const struct gsm0808_speech_codec *sc)
211{
212 /* See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
213 uint8_t header = 0;
214 uint8_t *old_tail;
Harald Welte459a1802018-06-28 09:24:17 +0200215 bool type_extended = false;
Philipp Maierbb839662017-06-01 17:11:19 +0200216
217 /* Note: Extended codec types are codec types that require 8 instead
218 * of 4 bit to fully specify the selected codec. In the following,
219 * we check if we work with an extended type or not. We also check
220 * if the codec type is valid at all. */
221 switch(sc->type) {
222 case GSM0808_SCT_FR1:
223 case GSM0808_SCT_FR2:
224 case GSM0808_SCT_FR3:
225 case GSM0808_SCT_FR4:
226 case GSM0808_SCT_FR5:
227 case GSM0808_SCT_HR1:
228 case GSM0808_SCT_HR3:
229 case GSM0808_SCT_HR4:
230 case GSM0808_SCT_HR6:
231 type_extended = false;
232 break;
233 case GSM0808_SCT_CSD:
234 type_extended = true;
235 break;
236 default:
237 /* Invalid codec type specified */
238 OSMO_ASSERT(false);
239 break;
240 }
Philipp Maier6f725d62017-03-24 18:03:17 +0100241
242 old_tail = msg->tail;
243
244 if (sc->fi)
245 header |= (1 << 7);
246 if (sc->pi)
247 header |= (1 << 6);
248 if (sc->pt)
249 header |= (1 << 5);
250 if (sc->tf)
251 header |= (1 << 4);
Philipp Maierbb839662017-06-01 17:11:19 +0200252
253 if (type_extended) {
Philipp Maier6f725d62017-03-24 18:03:17 +0100254 header |= 0x0f;
255 msgb_put_u8(msg, header);
Philipp Maierbb839662017-06-01 17:11:19 +0200256 msgb_put_u8(msg, sc->type);
Philipp Maier6f725d62017-03-24 18:03:17 +0100257 } else {
258 OSMO_ASSERT(sc->type < 0x0f);
259 header |= sc->type;
260 msgb_put_u8(msg, header);
Philipp Maier6f725d62017-03-24 18:03:17 +0100261 }
262
Philipp Maierbb839662017-06-01 17:11:19 +0200263 /* Note: Whether a configuration is present or not depends on the
264 * selected codec type. If present, it can either consist of one
265 * or two octets, depending on the codec type */
266 switch (sc->type) {
267 case GSM0808_SCT_FR3:
268 case GSM0808_SCT_HR3:
269 case GSM0808_SCT_HR6:
Philipp Maier7e27b142018-03-22 17:26:46 +0100270 msgb_put_u16(msg, osmo_ntohs(sc->cfg));
Philipp Maierbb839662017-06-01 17:11:19 +0200271 break;
272 case GSM0808_SCT_FR4:
273 case GSM0808_SCT_FR5:
274 case GSM0808_SCT_HR4:
275 case GSM0808_SCT_CSD:
Alexander Couzens4e284b62019-06-23 01:53:43 +0200276 OSMO_ASSERT((sc->cfg & 0xff00) == 0);
Philipp Maierbb839662017-06-01 17:11:19 +0200277 msgb_put_u8(msg, (uint8_t) sc->cfg & 0xff);
278 break;
279 default:
280 OSMO_ASSERT(sc->cfg == 0);
281 break;
282 }
Philipp Maier6f725d62017-03-24 18:03:17 +0100283
284 return (uint8_t) (msg->tail - old_tail);
285}
286
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200287/*! Encode TS 08.08 Speech Codec IE
Harald Welte96e2a002017-06-12 21:44:18 +0200288 * \param[out] msg Message Buffer to which IE will be appended
289 * \param[in] sc Speech Codec to be encoded into IE
290 * \returns number of bytes appended to \a msg */
Philipp Maier6f725d62017-03-24 18:03:17 +0100291uint8_t gsm0808_enc_speech_codec(struct msgb *msg,
292 const struct gsm0808_speech_codec *sc)
293{
Philipp Maier452a6bb2017-06-23 00:29:34 +0200294 /*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
Philipp Maier6f725d62017-03-24 18:03:17 +0100295 uint8_t *old_tail;
296 uint8_t *tlv_len;
297
298 OSMO_ASSERT(msg);
299 OSMO_ASSERT(sc);
300
301 msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC);
302 tlv_len = msgb_put(msg, 1);
303 old_tail = msg->tail;
304
305 enc_speech_codec(msg, sc);
306
307 *tlv_len = (uint8_t) (msg->tail - old_tail);
308 return *tlv_len + 2;
309}
310
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200311/*! Decode TS 08.08 Speech Codec IE
Harald Welte96e2a002017-06-12 21:44:18 +0200312 * \param[out] sc Caller-allocated memory for Speech Codec
313 * \param[in] elem IE value to be decoded
314 * \param[in] len Length of \a elem in bytes
315 * \returns number of bytes parsed; negative on error */
Philipp Maier6f725d62017-03-24 18:03:17 +0100316int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
317 const uint8_t *elem, uint8_t len)
318{
319 /* See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
320 uint8_t header;
321 const uint8_t *old_elem = elem;
322
323 OSMO_ASSERT(sc);
324 if (!elem)
325 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +0200326 if (len == 0)
Philipp Maier6f725d62017-03-24 18:03:17 +0100327 return -EINVAL;
328
329 memset(sc, 0, sizeof(*sc));
330
331 header = *elem;
332
Philipp Maier85a6af22017-04-28 10:55:05 +0200333 /* An extended codec type needs at least two fields,
334 * bail if the input data length is not sufficient. */
Philipp Maier6f725d62017-03-24 18:03:17 +0100335 if ((header & 0x0F) == 0x0F && len < 2)
336 return -EINVAL;
Philipp Maier6f725d62017-03-24 18:03:17 +0100337
338 elem++;
339 len--;
340
341 if (header & (1 << 7))
342 sc->fi = true;
343 if (header & (1 << 6))
344 sc->pi = true;
345 if (header & (1 << 5))
346 sc->pt = true;
347 if (header & (1 << 4))
348 sc->tf = true;
349
350 if ((header & 0x0F) != 0x0F) {
351 sc->type = (header & 0x0F);
Philipp Maierbb839662017-06-01 17:11:19 +0200352 } else {
353 sc->type = *elem;
354 elem++;
355 len--;
Philipp Maier6f725d62017-03-24 18:03:17 +0100356 }
357
Philipp Maierbb839662017-06-01 17:11:19 +0200358 /* Note: Whether a configuration is present or not depends on the
359 * selected codec type. If present, it can either consist of one or
360 * two octets depending on the codec type */
361 switch (sc->type) {
362 case GSM0808_SCT_FR1:
363 case GSM0808_SCT_FR2:
364 case GSM0808_SCT_HR1:
365 break;
366 case GSM0808_SCT_HR4:
367 case GSM0808_SCT_CSD:
368 case GSM0808_SCT_FR4:
369 case GSM0808_SCT_FR5:
370 if (len < 1)
371 return -EINVAL;
372 sc->cfg = *elem;
373 elem++;
374 break;
375 case GSM0808_SCT_FR3:
376 case GSM0808_SCT_HR3:
377 case GSM0808_SCT_HR6:
378 if (len < 2)
379 return -EINVAL;
Philipp Maier7e27b142018-03-22 17:26:46 +0100380 sc->cfg = osmo_load16le(elem);
Philipp Maierbb839662017-06-01 17:11:19 +0200381 elem += 2;
382 break;
383 default:
384 /* Invalid codec type => malformed speech codec element! */
385 return -EINVAL;
386 break;
387 }
Philipp Maier6f725d62017-03-24 18:03:17 +0100388
389 return (int)(elem - old_elem);
390}
391
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200392/*! Encode TS 08.08 Speech Codec list
Harald Welte96e2a002017-06-12 21:44:18 +0200393 * \param[out] msg Message Buffer to which IE is to be appended
394 * \param[in] scl Speech Codec List to be encoded into IE
395 * \returns number of bytes added to \a msg */
Philipp Maier6f725d62017-03-24 18:03:17 +0100396uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg,
397 const struct gsm0808_speech_codec_list *scl)
398{
Philipp Maier452a6bb2017-06-23 00:29:34 +0200399 /*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
Philipp Maier6f725d62017-03-24 18:03:17 +0100400 uint8_t *old_tail;
401 uint8_t *tlv_len;
402 unsigned int i;
403 uint8_t rc;
404 unsigned int bytes_used = 0;
405
406 OSMO_ASSERT(msg);
407 OSMO_ASSERT(scl);
408
Philipp Maier6f725d62017-03-24 18:03:17 +0100409 msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC_LIST);
410 tlv_len = msgb_put(msg, 1);
411 old_tail = msg->tail;
412
413 for (i = 0; i < scl->len; i++) {
414 rc = enc_speech_codec(msg, &scl->codec[i]);
415 OSMO_ASSERT(rc >= 1);
416 bytes_used += rc;
417 OSMO_ASSERT(bytes_used <= 255);
418 }
419
420 *tlv_len = (uint8_t) (msg->tail - old_tail);
421 return *tlv_len + 2;
422}
423
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200424/*! Decode TS 08.08 Speech Codec list IE
Harald Welte96e2a002017-06-12 21:44:18 +0200425 * \param[out] scl Caller-provided memory to store codec list
426 * \param[in] elem IE value to be decoded
427 * \param[in] len Length of \a elem in bytes
428 * \returns number of bytes parsed; negative on error */
Philipp Maier6f725d62017-03-24 18:03:17 +0100429int gsm0808_dec_speech_codec_list(struct gsm0808_speech_codec_list *scl,
430 const uint8_t *elem, uint8_t len)
431{
Philipp Maier452a6bb2017-06-23 00:29:34 +0200432 /*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
Philipp Maier6f725d62017-03-24 18:03:17 +0100433 const uint8_t *old_elem = elem;
434 unsigned int i;
435 int rc;
436 uint8_t decoded = 0;
437
438 OSMO_ASSERT(scl);
439 if (!elem)
440 return -EINVAL;
Philipp Maier6f725d62017-03-24 18:03:17 +0100441
442 memset(scl, 0, sizeof(*scl));
443
444 for (i = 0; i < ARRAY_SIZE(scl->codec); i++) {
445 if (len <= 0)
446 break;
447
448 rc = gsm0808_dec_speech_codec(&scl->codec[i], elem, len);
449 if (rc < 1)
450 return -EINVAL;
451
452 elem+=rc;
453 len -= rc;
454 decoded++;
455 }
456
457 scl->len = decoded;
458
Philipp Maier6f725d62017-03-24 18:03:17 +0100459 return (int)(elem - old_elem);
460}
Philipp Maiere0c65302017-03-28 17:05:40 +0200461
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200462/*! Encode TS 08.08 Channel Type IE
Harald Welte96e2a002017-06-12 21:44:18 +0200463 * \param[out] msg Message Buffer to which IE is to be appended
464 * \param[in] ct Channel Type to be encoded
465 * \returns number of bytes added to \a msg */
Philipp Maiere0c65302017-03-28 17:05:40 +0200466uint8_t gsm0808_enc_channel_type(struct msgb *msg,
467 const struct gsm0808_channel_type *ct)
468{
Philipp Maier452a6bb2017-06-23 00:29:34 +0200469 /*! See also 3GPP TS 48.008 3.2.2.11 Channel Type */
Philipp Maiere0c65302017-03-28 17:05:40 +0200470 unsigned int i;
471 uint8_t byte;
472 uint8_t *old_tail;
473 uint8_t *tlv_len;
474
475 OSMO_ASSERT(msg);
476 OSMO_ASSERT(ct);
477 OSMO_ASSERT(ct->perm_spch_len <= CHANNEL_TYPE_ELEMENT_MAXLEN - 2);
478
479 /* FIXME: Implement encoding support for Data
480 * and Speech + CTM Text Telephony */
481 if ((ct->ch_indctr & 0x0f) != GSM0808_CHAN_SPEECH
482 && (ct->ch_indctr & 0x0f) != GSM0808_CHAN_SIGN)
483 OSMO_ASSERT(false);
484
485 msgb_put_u8(msg, GSM0808_IE_CHANNEL_TYPE);
486 tlv_len = msgb_put(msg, 1);
487 old_tail = msg->tail;
488
489 msgb_put_u8(msg, ct->ch_indctr & 0x0f);
490 msgb_put_u8(msg, ct->ch_rate_type);
491
492 for (i = 0; i < ct->perm_spch_len; i++) {
493 byte = ct->perm_spch[i];
494
495 if (i < ct->perm_spch_len - 1)
496 byte |= 0x80;
497 msgb_put_u8(msg, byte);
498 }
499
500 *tlv_len = (uint8_t) (msg->tail - old_tail);
501 return *tlv_len + 2;
502}
503
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200504/*! Decode TS 08.08 Channel Type IE
Harald Welte96e2a002017-06-12 21:44:18 +0200505 * \param[out] ct Caller-provided memory to store channel type
506 * \param[in] elem IE Value to be decoded
507 * \param[in] len Length of \a elem in bytes
508 * \returns number of bytes parsed; negative on error */
Philipp Maiere0c65302017-03-28 17:05:40 +0200509int gsm0808_dec_channel_type(struct gsm0808_channel_type *ct,
510 const uint8_t *elem, uint8_t len)
511{
Philipp Maier452a6bb2017-06-23 00:29:34 +0200512 /*! See also 3GPP TS 48.008 3.2.2.11 Channel Type */
Philipp Maiere0c65302017-03-28 17:05:40 +0200513 unsigned int i;
514 uint8_t byte;
515 const uint8_t *old_elem = elem;
516
517 OSMO_ASSERT(ct);
518 if (!elem)
519 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +0200520 if (len < 3 || len > 11)
Philipp Maiere0c65302017-03-28 17:05:40 +0200521 return -EINVAL;
522
523 memset(ct, 0, sizeof(*ct));
524
525 ct->ch_indctr = (*elem) & 0x0f;
526 elem++;
527 ct->ch_rate_type = (*elem) & 0x0f;
528 elem++;
529
530 for (i = 0; i < ARRAY_SIZE(ct->perm_spch); i++) {
531 byte = *elem;
532 elem++;
533 ct->perm_spch[i] = byte & 0x7f;
534 if ((byte & 0x80) == 0x00)
535 break;
536 }
537 ct->perm_spch_len = i + 1;
538
539 return (int)(elem - old_elem);
540}
Philipp Maier14e76b92017-03-28 18:36:52 +0200541
Max969fb2e2018-12-10 11:01:10 +0100542/*! Create BSSMAP Global Call Reference, 3GPP TS 48.008 §3.2.2.115.
543 * \param[out] msg Message Buffer for appending IE
544 * \param[in] g Global Call Reference, 3GPP TS 29.205 Table B 2.1.9.1
545 * \returns number of bytes added to \a msg or 0 on error */
Max47022152018-12-19 18:51:00 +0100546static uint8_t gsm0808_enc_gcr(struct msgb *msg, const struct osmo_gcr_parsed *g)
Max969fb2e2018-12-10 11:01:10 +0100547{
548 uint8_t enc, *len = msgb_tl_put(msg, GSM0808_IE_GLOBAL_CALL_REF);
549
550 enc = osmo_enc_gcr(msg, g);
551 if (!enc)
552 return 0;
553
554 *len = enc;
555 return enc + 2; /* type (1 byte) + length (1 byte) */
556}
557
558/*! Decode BSSMAP Global Call Reference, 3GPP TS 29.205 Table B 2.1.9.1.
559 * \param[out] gcr Caller-provided memory to store Global Call Reference
Max036012b2018-12-19 17:48:56 +0100560 * \param[in] tp IE values to be decoded
Max969fb2e2018-12-10 11:01:10 +0100561 * \returns number of bytes parsed; negative on error */
Max47022152018-12-19 18:51:00 +0100562static int gsm0808_dec_gcr(struct osmo_gcr_parsed *gcr, const struct tlv_parsed *tp)
Max969fb2e2018-12-10 11:01:10 +0100563{
564 int ret;
565 const uint8_t *buf = TLVP_VAL_MINLEN(tp, GSM0808_IE_GLOBAL_CALL_REF, OSMO_GCR_MIN_LEN);
566 if (!buf)
567 return -EINVAL;
568
569 ret = osmo_dec_gcr(gcr, buf, TLVP_LEN(tp, GSM0808_IE_GLOBAL_CALL_REF));
570 if (ret < 0)
571 return -ENOENT;
572
573 return 2 + ret;
574}
575
Max47022152018-12-19 18:51:00 +0100576/*! Add LCLS parameters to a given msgb, 3GPP TS 48.008 §3.2.2.115 - 3.2.2.120.
577 * \param[out] msg Message Buffer for appending IE
578 * \param[in] lcls LCLS-related data
579 * \returns number of bytes added to \a msg or 0 on error */
580uint8_t gsm0808_enc_lcls(struct msgb *msg, const struct osmo_lcls *lcls)
581{
582 uint8_t enc = 0;
583
584 /* LCLS: §3.2.2.115 Global Call Reference */
Max3b901252019-01-15 14:15:11 +0100585 if (lcls->gcr_available)
586 enc = gsm0808_enc_gcr(msg, &lcls->gcr);
Max47022152018-12-19 18:51:00 +0100587
588 /* LCLS: §3.2.2.116 Configuration */
589 if (lcls->config != GSM0808_LCLS_CFG_NA) {
590 msgb_tv_put(msg, GSM0808_IE_LCLS_CONFIG, lcls->config);
591 enc += 2;
592 }
593
594 /* LCLS: §3.2.2.117 Connection Status Control */
595 if (lcls->control != GSM0808_LCLS_CSC_NA) {
596 msgb_tv_put(msg, GSM0808_IE_LCLS_CONN_STATUS_CTRL, lcls->control);
597 enc += 2;
598 }
599
600 /* LCLS: §3.2.2.118 Correlation-Not-Needed */
601 if (!lcls->corr_needed) {
602 msgb_v_put(msg, GSM0808_IE_LCLS_CORR_NOT_NEEDED);
603 enc++;
604 }
605
606 return enc;
607}
608
609/*! Decode LCLS parameters to a given msgb, 3GPP TS 48.008 §3.2.2.115 - 3.2.2.120.
610 * \param[out] lcls Caller-provided memory to store LCLS-related data
611 * \param[in] tp IE values to be decoded
612 * \returns GCR size or negative on error */
613int gsm0808_dec_lcls(struct osmo_lcls *lcls, const struct tlv_parsed *tp)
614{
Max3b901252019-01-15 14:15:11 +0100615 int ret = gsm0808_dec_gcr(&lcls->gcr, tp);
Max47022152018-12-19 18:51:00 +0100616
Max3b901252019-01-15 14:15:11 +0100617 lcls->gcr_available = (ret < 0) ? false : true;
Max47022152018-12-19 18:51:00 +0100618 lcls->config = tlvp_val8(tp, GSM0808_IE_LCLS_CONFIG, GSM0808_LCLS_CFG_NA);
619 lcls->control = tlvp_val8(tp, GSM0808_IE_LCLS_CONN_STATUS_CTRL, GSM0808_LCLS_CSC_NA);
620 lcls->corr_needed = TLVP_PRESENT(tp, GSM0808_IE_LCLS_CORR_NOT_NEEDED) ? false : true;
621
622 return ret;
623}
624
Harald Welte171ef822019-03-28 10:49:05 +0100625static __thread char dbuf[256];
Max5ec0cf52019-01-15 16:37:09 +0100626
627/*! Dump LCLS parameters (GCR excluded) into string for printing.
Harald Welte4a62eda2019-03-18 18:27:00 +0100628 * \param[out] buf caller-allocated output string buffer
629 * \param[in] buf_len size of buf in bytes
Max5ec0cf52019-01-15 16:37:09 +0100630 * \param[in] lcls pointer to the struct to print.
631 * \returns string representation of LCLS or NULL on error. */
Harald Welte4a62eda2019-03-18 18:27:00 +0100632char *osmo_lcls_dump_buf(char *buf, size_t buf_len, const struct osmo_lcls *lcls)
Max5ec0cf52019-01-15 16:37:09 +0100633{
Harald Welte4a62eda2019-03-18 18:27:00 +0100634 struct osmo_strbuf s = { .buf = buf, .len = buf_len };
Max5ec0cf52019-01-15 16:37:09 +0100635
636 if (!lcls)
637 return NULL;
638
639 OSMO_STRBUF_PRINTF(s, "LCLS Config: %s, Control: %s, Correlation-Needed: %u",
640 gsm0808_lcls_config_name(lcls->config),
641 gsm0808_lcls_control_name(lcls->control),
642 lcls->corr_needed);
643
644 return dbuf;
645}
646
Harald Welte4a62eda2019-03-18 18:27:00 +0100647/*! Dump LCLS parameters (GCR excluded) into static string buffer for printing.
648 * \param[in] lcls pointer to the struct to print.
649 * \returns string representation of LCLS in static buffer or NULL on error. */
650char *osmo_lcls_dump(const struct osmo_lcls *lcls)
651{
652 return osmo_lcls_dump_buf(dbuf, sizeof(dbuf), lcls);
653}
654
Harald Welte179f3572019-03-18 18:38:47 +0100655char *osmo_lcls_dump_c(void *ctx, const struct osmo_lcls *lcls)
656{
657 char *buf = talloc_size(ctx, 256);
658 if (!buf)
659 return NULL;
660 return osmo_lcls_dump_buf(buf, 256, lcls);
661}
662
Max5ec0cf52019-01-15 16:37:09 +0100663/*! Dump GCR struct into string for printing.
Harald Welte4a62eda2019-03-18 18:27:00 +0100664 * \param[out] buf caller-allocated output string buffer
665 * \param[in] buf_len size of buf in bytes
Max5ec0cf52019-01-15 16:37:09 +0100666 * \param[in] lcls pointer to the struct to print.
667 * \returns string representation of GCR or NULL on error. */
Harald Welte4a62eda2019-03-18 18:27:00 +0100668char *osmo_gcr_dump_buf(char *buf, size_t buf_len, const struct osmo_lcls *lcls)
Max5ec0cf52019-01-15 16:37:09 +0100669{
Harald Welte4a62eda2019-03-18 18:27:00 +0100670 struct osmo_strbuf s = { .buf = buf, .len = buf_len };
Max5ec0cf52019-01-15 16:37:09 +0100671
672 if (!lcls)
673 return NULL;
674
675 if (lcls->gcr_available) {
676 OSMO_STRBUF_PRINTF(s, "GCR NetID 0x%s, ", osmo_hexdump_nospc(lcls->gcr.net, lcls->gcr.net_len));
677 /* osmo_hexdump() uses static buffers so we can't call it twice withing the same parameter list */
678 OSMO_STRBUF_PRINTF(s, "Node 0x%x, CallRefID 0x%s", lcls->gcr.node, osmo_hexdump_nospc(lcls->gcr.cr, 5));
679 }
680
681 return dbuf;
682}
683
Harald Welte4a62eda2019-03-18 18:27:00 +0100684/*! Dump GCR struct into static string buffer for printing.
685 * \param[in] lcls pointer to the struct to print.
686 * \returns string representation of GCR in static buffer or NULL on error. */
687char *osmo_gcr_dump(const struct osmo_lcls *lcls)
688{
689 return osmo_gcr_dump_buf(dbuf, sizeof(dbuf), lcls);
690}
691
692
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200693/*! Encode TS 08.08 Encryption Information IE
Harald Welte96e2a002017-06-12 21:44:18 +0200694 * \param[out] msg Message Buffer to which IE is to be appended
695 * \param[in] ei Encryption Information to be encoded
696 * \returns number of bytes appended to \a msg */
Philipp Maier14e76b92017-03-28 18:36:52 +0200697uint8_t gsm0808_enc_encrypt_info(struct msgb *msg,
698 const struct gsm0808_encrypt_info *ei)
699{
700 unsigned int i;
701 uint8_t perm_algo = 0;
702 uint8_t *ptr;
703 uint8_t *old_tail;
704 uint8_t *tlv_len;
705
706 OSMO_ASSERT(msg);
707 OSMO_ASSERT(ei);
708 OSMO_ASSERT(ei->key_len <= ARRAY_SIZE(ei->key));
709 OSMO_ASSERT(ei->perm_algo_len <= ENCRY_INFO_PERM_ALGO_MAXLEN);
710
711 msgb_put_u8(msg, GSM0808_IE_ENCRYPTION_INFORMATION);
712 tlv_len = msgb_put(msg, 1);
713 old_tail = msg->tail;
714
715 for (i = 0; i < ei->perm_algo_len; i++) {
716 /* Note: gsm_08_08.h defines the permitted algorithms
717 * as an enum which ranges from 0x01 to 0x08 */
718 OSMO_ASSERT(ei->perm_algo[i] != 0);
719 OSMO_ASSERT(ei->perm_algo[i] <= ENCRY_INFO_PERM_ALGO_MAXLEN);
720 perm_algo |= (1 << (ei->perm_algo[i] - 1));
721 }
722
723 msgb_put_u8(msg, perm_algo);
Neels Hofmeyr26e53b12021-06-10 00:47:03 +0200724 /* FIXME: 48.008 3.2.2.10 Encryption Information says:
725 * "When present, the key shall be 8 octets long." */
Philipp Maier14e76b92017-03-28 18:36:52 +0200726 ptr = msgb_put(msg, ei->key_len);
727 memcpy(ptr, ei->key, ei->key_len);
728
729 *tlv_len = (uint8_t) (msg->tail - old_tail);
730 return *tlv_len + 2;
731}
732
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200733/*! Decode TS 08.08 Encryption Information IE
Harald Welte96e2a002017-06-12 21:44:18 +0200734 * \param[out] ei Caller-provided memory to store encryption information
735 * \param[in] elem IE value to be decoded
736 * \param[in] len Length of \a elem in bytes
737 * \returns number of bytes parsed; negative on error */
Philipp Maier14e76b92017-03-28 18:36:52 +0200738int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
739 const uint8_t *elem, uint8_t len)
740{
741 uint8_t perm_algo;
742 unsigned int i;
743 unsigned int perm_algo_len = 0;
744 const uint8_t *old_elem = elem;
745
746 OSMO_ASSERT(ei);
747 if (!elem)
748 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +0200749 if (len == 0)
Philipp Maier14e76b92017-03-28 18:36:52 +0200750 return -EINVAL;
751
752 memset(ei, 0, sizeof(*ei));
753
754 perm_algo = *elem;
755 elem++;
756
757 for (i = 0; i < ENCRY_INFO_PERM_ALGO_MAXLEN; i++) {
758 if (perm_algo & (1 << i)) {
759 ei->perm_algo[perm_algo_len] = i + 1;
760 perm_algo_len++;
761 }
762 }
763 ei->perm_algo_len = perm_algo_len;
764
Neels Hofmeyr26e53b12021-06-10 00:47:03 +0200765 /* FIXME: 48.008 3.2.2.10 Encryption Information says:
766 * "When present, the key shall be 8 octets long." */
Philipp Maier14e76b92017-03-28 18:36:52 +0200767 ei->key_len = len - 1;
768 memcpy(ei->key, elem, ei->key_len);
769 elem+=ei->key_len;
770
771 return (int)(elem - old_elem);
772}
Philipp Maier783047e2017-03-29 11:35:50 +0200773
Neels Hofmeyr4a9756c2021-06-10 00:48:15 +0200774/*! Encode TS 48.008 Kc128 IE.
775 * \param[out] msg Message Buffer to which IE is to be appended.
776 * \param[in] kc128 Pointer to 16 bytes of Kc128 key data.
777 * \returns number of bytes appended to msg */
778int gsm0808_enc_kc128(struct msgb *msg, const uint8_t *kc128)
779{
780 uint8_t *start = msg->tail;
781 msgb_tv_fixed_put(msg, GSM0808_IE_KC_128, 16, kc128);
782 return msg->tail - start;
783}
784
785/*! Decode TS 48.008 Kc128 IE.
786 * \param[out] kc128 Target buffer for received Kc128 key, 16 bytes long.
787 * \param[in] elem IE value to be decoded (without IE discriminator).
788 * \param[in] len Length of elem in bytes.
789 * \returns number of bytes parsed; negative on error */
790int gsm0808_dec_kc128(uint8_t *kc128, const uint8_t *elem, uint8_t len)
791{
792 if (len != 16)
793 return -EINVAL;
794 memcpy(kc128, elem, 16);
795 return len;
796}
797
Pau Espin Pedrol85a0f112021-02-15 16:25:33 +0100798/* Store individual Cell Identifier information in a CGI, without clearing the remaining ones.
799 * This is useful to supplement one CGI with information from more than one Cell Identifier,
800 * which in turn is useful to match Cell Identifiers of differing kinds to each other.
801 * Before first invocation, clear the *dst struct externally, this function does only write those members
802 * that are present in parameter u.
803 */
804static void cell_id_to_cgi(struct osmo_cell_global_id *dst,
805 enum CELL_IDENT discr, const union gsm0808_cell_id_u *u)
806{
807 switch (discr) {
808 case CELL_IDENT_WHOLE_GLOBAL:
809 *dst = u->global;
810 return;
811
812 case CELL_IDENT_WHOLE_GLOBAL_PS:
813 dst->lai = u->global_ps.rai.lac;
814 dst->cell_identity = u->global_ps.cell_identity;
815 return;
816
817 case CELL_IDENT_LAC_AND_CI:
818 dst->lai.lac = u->lac_and_ci.lac;
819 dst->cell_identity = u->lac_and_ci.ci;
820 return;
821
822 case CELL_IDENT_CI:
823 dst->cell_identity = u->ci;
824 return;
825
826 case CELL_IDENT_LAI_AND_LAC:
827 dst->lai = u->lai_and_lac;
828 return;
829
830 case CELL_IDENT_LAC:
831 dst->lai.lac = u->lac;
832 return;
833
Pau Espin Pedrolb5551ee2022-02-16 13:09:32 +0100834 case CELL_IDENT_SAI:
835 dst->lai = u->sai.lai;
836 return;
837
Pau Espin Pedrol85a0f112021-02-15 16:25:33 +0100838 case CELL_IDENT_NO_CELL:
839 case CELL_IDENT_BSS:
840 case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
841 case CELL_IDENT_UTRAN_RNC:
842 case CELL_IDENT_UTRAN_LAC_RNC:
843 /* No values to set. */
844 return;
845 }
846}
847
Harald Weltef2210032019-08-31 21:25:05 +0200848/* Return the size of the value part of a cell identifier of given type */
849int gsm0808_cell_id_size(enum CELL_IDENT discr)
850{
851 switch (discr) {
852 case CELL_IDENT_WHOLE_GLOBAL:
853 return 7;
854 case CELL_IDENT_LAC_AND_CI:
855 return 4;
856 case CELL_IDENT_CI:
857 return 2;
858 case CELL_IDENT_LAI_AND_LAC:
859 return 5;
860 case CELL_IDENT_LAC:
861 return 2;
862 case CELL_IDENT_BSS:
863 case CELL_IDENT_NO_CELL:
864 return 0;
Pau Espin Pedrolb5551ee2022-02-16 13:09:32 +0100865 case CELL_IDENT_SAI:
866 return 7;
Pau Espin Pedrolca33a712021-01-05 19:36:48 +0100867 case CELL_IDENT_WHOLE_GLOBAL_PS:
868 return 8;
Harald Weltef2210032019-08-31 21:25:05 +0200869 default:
870 return -EINVAL;
871 }
872}
Harald Welteef7be492019-05-03 21:01:13 +0200873/*! Decode a single GSM 08.08 Cell ID list element payload
874 * \param[out] out caller-provided output union
875 * \param[in] discr Cell ID discriminator describing type to be decoded
876 * \param[in] buf User-provided input buffer containing binary Cell ID list element
877 * \param[in] len Length of buf
878 * \returns 0 on success; negative on error */
879int gsm0808_decode_cell_id_u(union gsm0808_cell_id_u *out, enum CELL_IDENT discr, const uint8_t *buf, unsigned int len)
880{
881 switch (discr) {
882 case CELL_IDENT_WHOLE_GLOBAL:
883 if (len < 7)
884 return -EINVAL;
885 decode_lai(buf, &out->global.lai);
886 out->global.cell_identity = osmo_load16be(buf + sizeof(struct gsm48_loc_area_id));
887 break;
888 case CELL_IDENT_LAC_AND_CI:
889 if (len < 4)
890 return -EINVAL;
891 out->lac_and_ci.lac = osmo_load16be(buf);
892 out->lac_and_ci.ci = osmo_load16be(buf + sizeof(uint16_t));
893 break;
894 case CELL_IDENT_CI:
895 if (len < 2)
896 return -EINVAL;
897 out->ci = osmo_load16be(buf);
898 break;
899 case CELL_IDENT_LAI_AND_LAC:
900 if (len < 5)
901 return -EINVAL;
902 decode_lai(buf, &out->lai_and_lac);
903 break;
904 case CELL_IDENT_LAC:
905 if (len < 2)
906 return -EINVAL;
907 out->lac = osmo_load16be(buf);
908 break;
909 case CELL_IDENT_BSS:
910 case CELL_IDENT_NO_CELL:
911 /* Does not have any list items */
912 break;
Pau Espin Pedrolb5551ee2022-02-16 13:09:32 +0100913 case CELL_IDENT_SAI:
914 if (len < 7)
915 return -EINVAL;
916 decode_lai(buf, &out->sai.lai);
917 out->sai.sac = osmo_load16be(buf + sizeof(struct gsm48_loc_area_id));
918 break;
Pau Espin Pedrolca33a712021-01-05 19:36:48 +0100919 case CELL_IDENT_WHOLE_GLOBAL_PS:
Pau Espin Pedrol52489852021-02-15 16:26:37 +0100920 /* 3GPP TS 48.018 sec 11.3.9 "Cell Identifier" */
Pau Espin Pedrolca33a712021-01-05 19:36:48 +0100921 if (len < 8)
922 return -EINVAL;
923 decode_lai(buf, (struct osmo_location_area_id *)&out->global_ps.rai); /* rai contains lai + non-decoded rac */
924 out->global_ps.rai.rac = *(buf + sizeof(struct gsm48_loc_area_id));
925 out->global_ps.cell_identity = osmo_load16be(buf + sizeof(struct gsm48_loc_area_id) + 1);
926 break;
Harald Welteef7be492019-05-03 21:01:13 +0200927 default:
928 /* Remaining cell identification types are not implemented. */
929 return -EINVAL;
930 }
931 return 0;
932}
933
Harald Weltee87693c2019-05-03 20:06:50 +0200934void gsm0808_msgb_put_cell_id_u(struct msgb *msg, enum CELL_IDENT id_discr, const union gsm0808_cell_id_u *u)
935{
936 switch (id_discr) {
937 case CELL_IDENT_WHOLE_GLOBAL: {
938 const struct osmo_cell_global_id *id = &u->global;
939 struct gsm48_loc_area_id lai;
940 gsm48_generate_lai2(&lai, &id->lai);
941 memcpy(msgb_put(msg, sizeof(lai)), &lai, sizeof(lai));
942 msgb_put_u16(msg, id->cell_identity);
943 break;
944 }
945 case CELL_IDENT_LAC_AND_CI: {
946 const struct osmo_lac_and_ci_id *id = &u->lac_and_ci;
947 msgb_put_u16(msg, id->lac);
948 msgb_put_u16(msg, id->ci);
949 break;
950 }
951 case CELL_IDENT_CI:
952 msgb_put_u16(msg, u->ci);
953 break;
954 case CELL_IDENT_LAI_AND_LAC: {
955 const struct osmo_location_area_id *id = &u->lai_and_lac;
956 struct gsm48_loc_area_id lai;
957 gsm48_generate_lai2(&lai, id);
958 memcpy(msgb_put(msg, sizeof(lai)), &lai, sizeof(lai));
959 break;
960 }
961 case CELL_IDENT_LAC:
962 msgb_put_u16(msg, u->lac);
963 break;
964 case CELL_IDENT_BSS:
965 case CELL_IDENT_NO_CELL:
966 /* Does not have any list items */
967 break;
Pau Espin Pedrolb5551ee2022-02-16 13:09:32 +0100968
969 case CELL_IDENT_SAI: {
970 const struct osmo_service_area_id *id = &u->sai;
971 struct gsm48_loc_area_id lai;
972 gsm48_generate_lai2(&lai, &id->lai);
973 memcpy(msgb_put(msg, sizeof(lai)), &lai, sizeof(lai));
974 msgb_put_u16(msg, id->sac);
975 break;
976 }
977
Pau Espin Pedrolca33a712021-01-05 19:36:48 +0100978 case CELL_IDENT_WHOLE_GLOBAL_PS: {
Pau Espin Pedrol52489852021-02-15 16:26:37 +0100979 /* 3GPP TS 48.018 sec 11.3.9 "Cell Identifier" */
Pau Espin Pedrolca33a712021-01-05 19:36:48 +0100980 const struct osmo_cell_global_id_ps *id = &u->global_ps;
981 struct gsm48_loc_area_id lai;
982 gsm48_generate_lai2(&lai, &id->rai.lac);
983 memcpy(msgb_put(msg, sizeof(lai)), &lai, sizeof(lai));
984 memcpy(msgb_put(msg, 1), &id->rai.rac, 1);
985 msgb_put_u16(msg, id->cell_identity);
986 break;
987 }
Harald Weltee87693c2019-05-03 20:06:50 +0200988 default:
989 /* Support for other identifier list types is not implemented. */
990 OSMO_ASSERT(false);
991 }
992}
993
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200994/*! Encode TS 08.08 Cell Identifier List IE
Harald Welte96e2a002017-06-12 21:44:18 +0200995 * \param[out] msg Message Buffer to which IE is to be appended
996 * \param[in] cil Cell ID List to be encoded
997 * \returns number of bytes appended to \a msg */
Stefan Sperling11a4d9d2018-02-15 18:28:04 +0100998uint8_t gsm0808_enc_cell_id_list2(struct msgb *msg,
999 const struct gsm0808_cell_id_list2 *cil)
1000{
1001 uint8_t *old_tail;
1002 uint8_t *tlv_len;
1003 unsigned int i;
Pau Espin Pedrol52489852021-02-15 16:26:37 +01001004 uint8_t id_discr;
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001005
1006 OSMO_ASSERT(msg);
1007 OSMO_ASSERT(cil);
1008
1009 msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST);
1010 tlv_len = msgb_put(msg, 1);
1011 old_tail = msg->tail;
1012
Pau Espin Pedrol52489852021-02-15 16:26:37 +01001013 /* CGI-PS is an osmocom-specific type. In here we don't care about the
1014 * PS part, only the CS one. */
1015 if (cil->id_discr == CELL_IDENT_WHOLE_GLOBAL_PS)
1016 id_discr = CELL_IDENT_WHOLE_GLOBAL;
1017 else
1018 id_discr = cil->id_discr & 0x0f;
1019
1020 msgb_put_u8(msg, id_discr);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001021
Alexander Couzens4e284b62019-06-23 01:53:43 +02001022 OSMO_ASSERT(cil->id_list_len <= GSM0808_CELL_ID_LIST2_MAXLEN);
Pau Espin Pedrol52489852021-02-15 16:26:37 +01001023 for (i = 0; i < cil->id_list_len; i++) {
1024 if (cil->id_discr == CELL_IDENT_WHOLE_GLOBAL_PS) {
1025 union gsm0808_cell_id_u u;
1026 cell_id_to_cgi(&u.global, cil->id_discr, &cil->id_list[i]);
1027 gsm0808_msgb_put_cell_id_u(msg, CELL_IDENT_WHOLE_GLOBAL, &u);
1028 } else {
1029 gsm0808_msgb_put_cell_id_u(msg, cil->id_discr, &cil->id_list[i]);
1030 }
1031 }
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001032
1033 *tlv_len = (uint8_t) (msg->tail - old_tail);
1034 return *tlv_len + 2;
1035}
1036
1037/*! DEPRECATED: Use gsm0808_enc_cell_id_list2 instead.
1038 *
1039 * Encode TS 08.08 Cell Identifier List IE
1040 * \param[out] msg Message Buffer to which IE is to be appended
1041 * \param[in] cil Cell ID List to be encoded
1042 * \returns number of bytes appended to \a msg */
Philipp Maier783047e2017-03-29 11:35:50 +02001043uint8_t gsm0808_enc_cell_id_list(struct msgb *msg,
1044 const struct gsm0808_cell_id_list *cil)
1045{
1046 uint8_t *old_tail;
1047 uint8_t *tlv_len;
1048 unsigned int i;
1049
1050 OSMO_ASSERT(msg);
1051 OSMO_ASSERT(cil);
1052
1053 msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST);
1054 tlv_len = msgb_put(msg, 1);
1055 old_tail = msg->tail;
1056
1057 msgb_put_u8(msg, cil->id_discr & 0x0f);
1058
1059 switch (cil->id_discr) {
1060 case CELL_IDENT_LAC:
Alexander Couzens4e284b62019-06-23 01:53:43 +02001061 OSMO_ASSERT(cil->id_list_len <= CELL_ID_LIST_LAC_MAXLEN);
Philipp Maier783047e2017-03-29 11:35:50 +02001062 for (i=0;i<cil->id_list_len;i++) {
1063 msgb_put_u16(msg, cil->id_list_lac[i]);
1064 }
1065 break;
1066 case CELL_IDENT_BSS:
1067 /* Does not have any list items */
1068 break;
1069 default:
1070 /* FIXME: Implement support for all identifier list elements */
1071 OSMO_ASSERT(false);
1072 }
1073
1074 *tlv_len = (uint8_t) (msg->tail - old_tail);
1075 return *tlv_len + 2;
1076}
1077
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001078static 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 +01001079 size_t *consumed)
1080{
1081 struct osmo_cell_global_id *id;
1082 uint16_t *ci_be;
1083 size_t lai_offset;
1084 int i = 0;
1085 const size_t elemlen = sizeof(struct gsm48_loc_area_id) + sizeof(*ci_be);
1086
1087 *consumed = 0;
1088 while (remain >= elemlen) {
1089 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
1090 return -ENOSPC;
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001091 id = &cil->id_list[i].global;
Stefan Sperling2873bf12018-03-14 18:38:41 +01001092 lai_offset = i * elemlen;
Stefan Sperling23381452018-03-15 19:38:15 +01001093 decode_lai(&data[lai_offset], &id->lai);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001094 ci_be = (uint16_t *)(&data[lai_offset + sizeof(struct gsm48_loc_area_id)]);
1095 id->cell_identity = osmo_load16be(ci_be);
1096 *consumed += elemlen;
1097 remain -= elemlen;
1098 i++;
1099 }
1100
1101 return i;
1102}
1103
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001104static 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 +01001105 size_t *consumed)
1106{
1107 uint16_t *lacp_be, *ci_be;
1108 struct osmo_lac_and_ci_id *id;
Stefan Sperlinged4327c2018-03-16 11:02:59 +01001109 int i = 0, j = 0;
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001110 const size_t elemlen = sizeof(*lacp_be) + sizeof(*ci_be);
1111
1112 *consumed = 0;
1113
1114 if (remain < elemlen)
1115 return -EINVAL;
1116
Stefan Sperlinged4327c2018-03-16 11:02:59 +01001117 lacp_be = (uint16_t *)(&data[j]);
1118 ci_be = (uint16_t *)(&data[j + elemlen/2]);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001119 while (remain >= elemlen) {
1120 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
1121 return -ENOSPC;
Stefan Sperlinged4327c2018-03-16 11:02:59 +01001122 id = &cil->id_list[i++].lac_and_ci;
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001123 id->lac = osmo_load16be(lacp_be);
1124 id->ci = osmo_load16be(ci_be);
1125 *consumed += elemlen;
1126 remain -= elemlen;
Stefan Sperlinged4327c2018-03-16 11:02:59 +01001127 j += elemlen;
1128 lacp_be = (uint16_t *)(&data[j]);
1129 ci_be = (uint16_t *)(&data[j + elemlen/2]);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001130 }
1131
1132 return i;
1133}
1134
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001135static int parse_cell_id_ci_list(struct gsm0808_cell_id_list2 *cil, const uint8_t *data, size_t remain,
1136 size_t *consumed)
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001137{
1138 const uint16_t *ci_be = (const uint16_t *)data;
1139 int i = 0;
1140 const size_t elemlen = sizeof(*ci_be);
1141
1142 *consumed = 0;
1143 while (remain >= elemlen) {
1144 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
1145 return -ENOSPC;
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001146 cil->id_list[i++].ci = osmo_load16be(ci_be++);
Stefan Sperling9c62fc62018-03-16 10:23:34 +01001147 *consumed += elemlen;
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001148 remain -= elemlen;
1149 }
1150 return i;
1151}
1152
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001153static 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 +01001154 size_t *consumed)
1155{
1156 struct osmo_location_area_id *id;
1157 int i = 0;
1158 const size_t elemlen = sizeof(struct gsm48_loc_area_id);
1159
1160 *consumed = 0;
1161 while (remain >= elemlen) {
1162 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
1163 return -ENOSPC;
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001164 id = &cil->id_list[i].lai_and_lac;
Stefan Sperling23381452018-03-15 19:38:15 +01001165 decode_lai(&data[i * elemlen], id);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001166 *consumed += elemlen;
1167 remain -= elemlen;
1168 i++;
1169 }
1170
1171 return i;
1172}
1173
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001174static 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 +01001175{
1176 const uint16_t *lac_be = (const uint16_t *)data;
1177 int i = 0;
1178 const size_t elemlen = sizeof(*lac_be);
1179
1180 *consumed = 0;
1181 while (remain >= elemlen) {
1182 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
1183 return -ENOSPC;
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001184 cil->id_list[i++].lac = osmo_load16be(lac_be++);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001185 *consumed += elemlen;
1186 remain -= elemlen;
1187 }
1188 return i;
1189}
1190
Pau Espin Pedrolb5551ee2022-02-16 13:09:32 +01001191static int parse_cell_id_sai_list(struct gsm0808_cell_id_list2 *cil, const uint8_t *data, size_t remain, size_t *consumed)
1192{
1193 struct osmo_service_area_id *id;
1194 uint16_t *sac_be;
1195 size_t lai_offset;
1196 int i = 0;
1197 const size_t elemlen = sizeof(struct gsm48_loc_area_id) + sizeof(*sac_be);
1198
1199 *consumed = 0;
1200 while (remain >= elemlen) {
1201 if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
1202 return -ENOSPC;
1203 id = &cil->id_list[i].sai;
1204 lai_offset = i * elemlen;
1205 decode_lai(&data[lai_offset], &id->lai);
1206 sac_be = (uint16_t *)(&data[lai_offset + sizeof(struct gsm48_loc_area_id)]);
1207 id->sac = osmo_load16be(sac_be);
1208 *consumed += elemlen;
1209 remain -= elemlen;
1210 i++;
1211 }
1212
1213 return i;
1214}
1215
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001216/*! Decode Cell Identifier List IE
Harald Welte96e2a002017-06-12 21:44:18 +02001217 * \param[out] cil Caller-provided memory to store Cell ID list
1218 * \param[in] elem IE value to be decoded
1219 * \param[in] len Length of \a elem in bytes
1220 * \returns number of bytes parsed; negative on error */
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001221int gsm0808_dec_cell_id_list2(struct gsm0808_cell_id_list2 *cil,
1222 const uint8_t *elem, uint8_t len)
1223{
1224 uint8_t id_discr;
1225 size_t bytes_elem = 0;
1226 int list_len = 0;
1227
1228 OSMO_ASSERT(cil);
1229 if (!elem)
1230 return -EINVAL;
1231 if (len == 0)
1232 return -EINVAL;
1233
1234 memset(cil, 0, sizeof(*cil));
1235
1236 id_discr = *elem & 0x0f;
1237 elem++;
1238 len--;
1239
1240 switch (id_discr) {
1241 case CELL_IDENT_WHOLE_GLOBAL:
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001242 list_len = parse_cell_id_global_list(cil, elem, len, &bytes_elem);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001243 break;
1244 case CELL_IDENT_LAC_AND_CI:
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001245 list_len = parse_cell_id_lac_and_ci_list(cil, elem, len, &bytes_elem);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001246 break;
1247 case CELL_IDENT_CI:
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001248 list_len = parse_cell_id_ci_list(cil, elem, len, &bytes_elem);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001249 break;
1250 case CELL_IDENT_LAI_AND_LAC:
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001251 list_len = parse_cell_id_lai_and_lac(cil, elem, len, &bytes_elem);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001252 break;
1253 case CELL_IDENT_LAC:
Stefan Sperlinge1a86742018-03-15 18:05:02 +01001254 list_len = parse_cell_id_lac_list(cil, elem, len, &bytes_elem);
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001255 break;
1256 case CELL_IDENT_BSS:
1257 case CELL_IDENT_NO_CELL:
1258 /* Does not have any list items */
1259 break;
Pau Espin Pedrolb5551ee2022-02-16 13:09:32 +01001260 case CELL_IDENT_SAI:
1261 list_len = parse_cell_id_sai_list(cil, elem, len, &bytes_elem);
1262 break;
1263 case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
1264 case CELL_IDENT_UTRAN_RNC:
1265 case CELL_IDENT_UTRAN_LAC_RNC:
Stefan Sperling11a4d9d2018-02-15 18:28:04 +01001266 default:
1267 /* Remaining cell identification types are not implemented. */
1268 return -EINVAL;
1269 }
1270
1271 if (list_len < 0) /* parsing error */
1272 return list_len;
1273
1274 cil->id_discr = id_discr;
1275 cil->id_list_len = list_len;
1276
1277 /* One byte for the cell ID discriminator + any remaining bytes in
1278 * the IE which were consumed by the parser functions above. */
1279 return 1 + (int)bytes_elem;
1280}
1281
1282/*! DEPRECATED: Use gsm0808_dec_cell_id_list2 instead.
1283 *
1284 * Decode Cell Identifier List IE
1285 * \param[out] cil Caller-provided memory to store Cell ID list
1286 * \param[in] elem IE value to be decoded
1287 * \param[in] len Length of \a elem in bytes
1288 * \returns number of bytes parsed; negative on error */
Philipp Maier783047e2017-03-29 11:35:50 +02001289int gsm0808_dec_cell_id_list(struct gsm0808_cell_id_list *cil,
1290 const uint8_t *elem, uint8_t len)
1291{
1292 uint8_t id_discr;
1293 const uint8_t *old_elem = elem;
1294 unsigned int item_count = 0;
1295
1296 OSMO_ASSERT(cil);
1297 if (!elem)
1298 return -EINVAL;
Philipp Maier17778bd2017-04-28 11:05:44 +02001299 if (len == 0)
Philipp Maier783047e2017-03-29 11:35:50 +02001300 return -EINVAL;
1301
1302 memset(cil, 0, sizeof(*cil));
1303
1304 id_discr = *elem & 0x0f;
1305 elem++;
1306 len--;
1307
1308 cil->id_discr = id_discr;
1309
1310 switch (id_discr) {
1311 case CELL_IDENT_LAC:
1312 while (len >= 2) {
1313 cil->id_list_lac[item_count] = osmo_load16be(elem);
1314 elem += 2;
1315 item_count++;
1316 len -= 2;
1317 }
1318 case CELL_IDENT_BSS:
1319 /* Does not have any list items */
1320 break;
1321 default:
1322 /* FIXME: Implement support for all identifier list elements */
1323 return -EINVAL;
1324 }
1325
1326 cil->id_list_len = item_count;
1327 return (int)(elem - old_elem);
1328}
Harald Welte96e2a002017-06-12 21:44:18 +02001329
Neels Hofmeyr74663d92018-03-23 01:46:42 +01001330static bool same_cell_id_list_entries(const struct gsm0808_cell_id_list2 *a, int ai,
1331 const struct gsm0808_cell_id_list2 *b, int bi)
1332{
1333 struct gsm0808_cell_id_list2 tmp = {
1334 .id_discr = a->id_discr,
1335 .id_list_len = 1,
1336 };
1337 uint8_t buf_a[32 + sizeof(struct msgb)];
1338 uint8_t buf_b[32 + sizeof(struct msgb)];
1339 struct msgb *msg_a = (void*)buf_a;
1340 struct msgb *msg_b = (void*)buf_b;
1341
1342 msg_a->data_len = 32;
1343 msg_b->data_len = 32;
1344 msgb_reset(msg_a);
1345 msgb_reset(msg_b);
1346
1347 if (a->id_discr != b->id_discr)
1348 return false;
1349 if (ai >= a->id_list_len
1350 || bi >= b->id_list_len)
1351 return false;
1352
1353 tmp.id_list[0] = a->id_list[ai];
1354 gsm0808_enc_cell_id_list2(msg_a, &tmp);
1355
1356 tmp.id_list[0] = b->id_list[bi];
1357 gsm0808_enc_cell_id_list2(msg_b, &tmp);
1358
1359 if (msg_a->len != msg_b->len)
1360 return false;
1361 if (memcmp(msg_a->data, msg_b->data, msg_a->len))
1362 return false;
1363
1364 return true;
1365}
1366
1367/*! Append entries from one Cell Identifier List to another.
1368 * The cell identifier types must be identical between the two lists.
1369 * \param dst[out] Append entries to this list.
1370 * \param src[in] Append these entries to \a dst.
1371 * \returns the nr of items added, or negative on error: -EINVAL if the id_discr mismatch
1372 * between the lists, -ENOSPC if the destination list does not have enough space. If an error is
1373 * returned, \a dst may have already been changed (particularly on -ENOSPC). Note that a return value
1374 * of zero may occur when the src->id_list_len is zero, or when all entries from \a src already exist
1375 * in \a dst, and does not indicate error per se. */
1376int gsm0808_cell_id_list_add(struct gsm0808_cell_id_list2 *dst, const struct gsm0808_cell_id_list2 *src)
1377{
1378 int i, j;
1379 int added = 0;
1380
1381 if (dst->id_list_len == 0
1382 && dst->id_discr != CELL_IDENT_BSS)
1383 dst->id_discr = src->id_discr;
1384 else if (dst->id_discr != src->id_discr)
1385 return -EINVAL;
1386
1387 for (i = 0; i < src->id_list_len; i++) {
1388 /* don't add duplicate entries */
1389 bool skip = false;
1390 for (j = 0; j < dst->id_list_len; j++) {
1391 if (same_cell_id_list_entries(dst, j, src, i)) {
1392 skip = true;
1393 break;
1394 }
1395 }
1396 if (skip)
1397 continue;
1398
1399 if (dst->id_list_len >= ARRAY_SIZE(dst->id_list))
1400 return -ENOSPC;
1401
1402 dst->id_list[dst->id_list_len++] = src->id_list[i];
1403 added ++;
1404 }
1405
1406 return added;
1407}
1408
Neels Hofmeyr38e58412018-05-25 16:56:35 +02001409/*! Convert a single Cell Identifier to a Cell Identifier List with one entry.
1410 * \param dst[out] Overwrite this list.
1411 * \param src[in] Set \a dst to contain exactly this item.
1412 */
1413void gsm0808_cell_id_to_list(struct gsm0808_cell_id_list2 *dst, const struct gsm0808_cell_id *src)
1414{
1415 if (!dst)
1416 return;
1417 if (!src) {
1418 *dst = (struct gsm0808_cell_id_list2){
1419 .id_discr = CELL_IDENT_NO_CELL,
1420 };
1421 return;
1422 }
1423
1424 *dst = (struct gsm0808_cell_id_list2){
1425 .id_discr = src->id_discr,
1426 .id_list = { src->id },
1427 .id_list_len = 1,
1428 };
1429
1430 switch (src->id_discr) {
1431 case CELL_IDENT_NO_CELL:
1432 case CELL_IDENT_BSS:
1433 dst->id_list_len = 0;
1434 break;
1435 default:
1436 break;
1437 }
1438}
1439
Neels Hofmeyr250e7f72018-04-13 03:30:14 +02001440/*! Encode Cell Identifier IE (3GPP TS 48.008 3.2.2.17).
1441 * \param[out] msg Message Buffer to which IE is to be appended
1442 * \param[in] ci Cell ID to be encoded
1443 * \returns number of bytes appended to \a msg */
1444uint8_t gsm0808_enc_cell_id(struct msgb *msg, const struct gsm0808_cell_id *ci)
1445{
1446 uint8_t rc;
1447 uint8_t *ie_tag;
1448 struct gsm0808_cell_id_list2 cil = {
1449 .id_discr = ci->id_discr,
1450 .id_list = { ci->id },
1451 .id_list_len = 1,
1452 };
1453
1454 OSMO_ASSERT(msg);
1455 OSMO_ASSERT(ci);
1456
1457 ie_tag = msg->tail;
1458 rc = gsm0808_enc_cell_id_list2(msg, &cil);
1459
1460 if (rc <= 0)
1461 return rc;
1462
1463 *ie_tag = GSM0808_IE_CELL_IDENTIFIER;
1464 return rc;
1465}
1466
1467/*! Decode Cell Identifier IE (3GPP TS 48.008 3.2.2.17).
1468 * \param[out] ci Caller-provided memory to store Cell ID.
1469 * \param[in] elem IE value to be decoded.
1470 * \param[in] len Length of \a elem in bytes.
1471 * \returns number of bytes parsed; negative on error */
1472int gsm0808_dec_cell_id(struct gsm0808_cell_id *ci, const uint8_t *elem, uint8_t len)
1473{
1474 struct gsm0808_cell_id_list2 cil;
1475 int rc;
1476 rc = gsm0808_dec_cell_id_list2(&cil, elem, len);
1477 if (rc < 0)
1478 return rc;
1479 if (cil.id_discr == CELL_IDENT_BSS || cil.id_discr == CELL_IDENT_NO_CELL) {
1480 if (cil.id_list_len != 0)
1481 return -EINVAL;
1482 } else {
1483 if (cil.id_list_len != 1)
1484 return -EINVAL;
1485 }
1486 ci->id_discr = cil.id_discr;
1487 ci->id = cil.id_list[0];
1488 return rc;
1489}
1490
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001491/*! Convert the representation of the permitted speech codec identifier
Philipp Maier3149b0d2017-06-02 13:22:34 +02001492 * that is used in struct gsm0808_channel_type to the speech codec
1493 * representation we use in struct gsm0808_speech_codec.
1494 * \param[in] perm_spch to be converted (see also gsm0808_permitted_speech)
1495 * \returns GSM speech codec type; negative on error */
1496int gsm0808_chan_type_to_speech_codec(uint8_t perm_spch)
1497{
1498 /*! The speech codec type, which is used in the channel type field to
1499 * signal the permitted speech versions (codecs) has a different
1500 * encoding than the type field in the speech codec type element
1501 * (See also 3GPP TS 48.008, 3.2.2.11 and 3.2.2.103) */
1502
1503 switch (perm_spch) {
1504 case GSM0808_PERM_FR1:
1505 return GSM0808_SCT_FR1;
1506 case GSM0808_PERM_FR2:
1507 return GSM0808_SCT_FR2;
1508 case GSM0808_PERM_FR3:
1509 return GSM0808_SCT_FR3;
1510 case GSM0808_PERM_FR4:
1511 return GSM0808_SCT_FR4;
1512 case GSM0808_PERM_FR5:
1513 return GSM0808_SCT_FR5;
1514 case GSM0808_PERM_HR1:
1515 return GSM0808_SCT_HR1;
1516 case GSM0808_PERM_HR3:
1517 return GSM0808_SCT_HR3;
1518 case GSM0808_PERM_HR4:
1519 return GSM0808_SCT_HR4;
1520 case GSM0808_PERM_HR6:
1521 return GSM0808_SCT_HR6;
1522 }
1523
1524 /* Invalid input */
1525 return -EINVAL;
1526}
1527
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001528/*! Extrapolate a speech codec field from a given permitted speech
Philipp Maier884ba0f2017-06-02 13:49:16 +02001529 * parameter (channel type).
1530 * \param[out] sc Caller provided memory to store the resulting speech codec
1531 * \param[in] perm_spch value that is used to derive the speech codec info
1532 * (see also: enum gsm0808_speech_codec_type in gsm0808_utils.h)
1533 * \returns zero when successful; negative on error */
1534int gsm0808_speech_codec_from_chan_type(struct gsm0808_speech_codec *sc,
1535 uint8_t perm_spch)
1536{
1537 int rc;
1538
1539 memset(sc, 0, sizeof(*sc));
1540
1541 /* Determine codec type */
1542 rc = gsm0808_chan_type_to_speech_codec(perm_spch);
1543 if (rc < 0)
1544 return -EINVAL;
1545 sc->type = (uint8_t) rc;
1546
1547 /* Depending on the speech codec type, pick a default codec
1548 * configuration that exactly matches the configuration on the
1549 * air interface. */
1550 switch (sc->type) {
1551 case GSM0808_SCT_FR3:
1552 sc->cfg = GSM0808_SC_CFG_DEFAULT_FR_AMR;
1553 break;
1554 case GSM0808_SCT_FR4:
1555 sc->cfg = GSM0808_SC_CFG_DEFAULT_OFR_AMR_WB;
1556 break;
1557 case GSM0808_SCT_FR5:
1558 sc->cfg = GSM0808_SC_CFG_DEFAULT_FR_AMR_WB;
1559 break;
1560 case GSM0808_SCT_HR3:
1561 sc->cfg = GSM0808_SC_CFG_DEFAULT_HR_AMR;
1562 break;
1563 case GSM0808_SCT_HR4:
1564 sc->cfg = GSM0808_SC_CFG_DEFAULT_OHR_AMR_WB;
1565 break;
1566 case GSM0808_SCT_HR6:
1567 sc->cfg = GSM0808_SC_CFG_DEFAULT_OHR_AMR;
1568 break;
1569 default:
1570 /* Note: Not all codec types specify a default setting,
1571 * in this case, we just set the field to zero. */
1572 sc->cfg = 0;
1573 }
1574
1575 /* Tag all codecs as "Full IP"
1576 * (see als 3GPP TS 48.008 3.2.2.103) */
1577 sc->fi = true;
1578
1579 return 0;
1580}
1581
Philipp Maier5f2eb152018-09-19 13:40:21 +02001582/*! Determine a set of AMR speech codec configuration bits (S0-S15) from a
1583 * given GSM 04.08 AMR configuration struct.
1584 * \param[in] cfg AMR configuration in GSM 04.08 format.
1585 * \param[in] hint if the resulting configuration shall be used with a FR or HR TCH.
1586 * \returns configuration bits (S0-S15) */
Philipp Maier369015c2018-09-21 09:07:20 +02001587uint16_t gsm0808_sc_cfg_from_gsm48_mr_cfg(const struct gsm48_multi_rate_conf *cfg,
Philipp Maier5f2eb152018-09-19 13:40:21 +02001588 bool fr)
1589{
1590 uint16_t s15_s0 = 0;
1591
1592 /* Check each rate bit in the AMR multirate configuration and pick the
1593 * matching default configuration as specified in 3GPP TS 28.062,
1594 * Table 7.11.3.1.3-2. */
1595 if (cfg->m4_75)
1596 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_4_75;
1597 if (cfg->m5_15)
1598 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_5_15;
1599 if (cfg->m5_90)
1600 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_5_90;
1601 if (cfg->m6_70)
1602 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_6_70;
1603 if (cfg->m7_40)
1604 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_7_40;
1605 if (cfg->m7_95)
1606 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_7_95;
1607 if (cfg->m10_2)
1608 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_10_2;
1609 if (cfg->m12_2)
1610 s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_12_2;
1611
1612 /* Note: 3GPP TS 48.008, chapter 3GPP TS 48.008 states that for AMR
1613 * some of the configuration bits must be coded as zeros. The applied
1614 * bitmask matches the default codec settings. See also the definition
1615 * of enum gsm0808_speech_codec_defaults in gsm_08_08.h and
1616 * 3GPP TS 28.062, Table 7.11.3.1.3-2. */
1617 if (fr)
1618 s15_s0 &= GSM0808_SC_CFG_DEFAULT_FR_AMR;
1619 else
1620 s15_s0 &= GSM0808_SC_CFG_DEFAULT_HR_AMR;
1621
Philipp Maier94d79fd2019-03-01 10:40:48 +01001622 /* The mode that is encoded by S1 (Config-NB-Code = 1), takes a special
1623 * role as it does not stand for a single rate, but for up to four rates
1624 * at once (12.2, 7.4, 5.9, 4.75). We must check if the supplied cfg
1625 * covers this mode. If not, we need to make sure that the related
1626 * bit is removed. (See also 3GPP TS 28.062, Table 7.11.3.1.3-2) */
1627 if (!(cfg->m12_2 && cfg->m7_40 && cfg->m5_90 && cfg->m4_75) && fr)
1628 s15_s0 &= ~GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20;
1629 else if (!(cfg->m7_40 && cfg->m5_90 && cfg->m4_75))
1630 s15_s0 &= ~GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20;
1631
Philipp Maier5f2eb152018-09-19 13:40:21 +02001632 return s15_s0;
1633}
1634
Philipp Maier8515d032018-09-25 15:57:49 +02001635/*! Determine a GSM 04.08 AMR configuration struct from a set of speech codec
1636 * configuration bits (S0-S15)
1637 * \param[out] cfg AMR configuration in GSM 04.08 format.
Philipp Maier3713af82019-02-27 16:48:25 +01001638 * \param[in] s15_s0 configuration bits (S15-S0, non-ambiguous).
1639 * \returns zero when successful; negative on error */
1640int gsm48_mr_cfg_from_gsm0808_sc_cfg(struct gsm48_multi_rate_conf *cfg,
1641 uint16_t s15_s0)
Philipp Maier8515d032018-09-25 15:57:49 +02001642{
Philipp Maier3713af82019-02-27 16:48:25 +01001643 unsigned int count = 0;
1644
1645 /* Note: See also: 3GPP TS 28.062
1646 * Table 7.11.3.1.3-2: Preferred Configurations for the Adaptive
1647 * Multi-Rate Codec Types */
1648
1649 /* Note: The resulting multirate-configuration must not contain an
1650 * active set of more than four codec rates. The active set also
1651 * must contain at least one rate. */
1652
Philipp Maier8515d032018-09-25 15:57:49 +02001653 memset(cfg, 0, sizeof(*cfg));
Philipp Maier3713af82019-02-27 16:48:25 +01001654 cfg->ver = 1;
1655 cfg->icmi = 1;
Philipp Maier8515d032018-09-25 15:57:49 +02001656
1657 /* Strip option bits */
1658 s15_s0 &= 0x00ff;
1659
Philipp Maier3713af82019-02-27 16:48:25 +01001660 /* Rate 5,15k can never be selected (see table) */
1661 cfg->m5_15 = 0;
Philipp Maier8515d032018-09-25 15:57:49 +02001662
Neels Hofmeyra0f2b212021-04-14 22:45:13 +02001663 if (s15_s0 & GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20) {
Philipp Maier3713af82019-02-27 16:48:25 +01001664 /* Table Table 7.11.3.1.3-2 lists one mode that selects 4
1665 * rates at once (Config-NB-Code = 1). The rates selected
1666 * are known to be compatible between GERAN and UTRAN, since
1667 * an active set must not contain more than four rates at
1668 * a time, we ignore all other settings as they are either
1669 * redundaned or excess settings (invalid) */
Philipp Maier8515d032018-09-25 15:57:49 +02001670 cfg->m4_75 = 1;
Philipp Maier8515d032018-09-25 15:57:49 +02001671 cfg->m5_90 = 1;
Philipp Maier8515d032018-09-25 15:57:49 +02001672 cfg->m7_40 = 1;
Philipp Maier8515d032018-09-25 15:57:49 +02001673 cfg->m12_2 = 1;
Philipp Maier3713af82019-02-27 16:48:25 +01001674 count += 4;
1675 }
Philipp Maier8515d032018-09-25 15:57:49 +02001676
Philipp Maier3713af82019-02-27 16:48:25 +01001677 /* Check the bits in s15_s0 and set the flags for the
1678 * respective rates. */
1679 if (s15_s0 & GSM0808_SC_CFG_AMR_4_75 && !cfg->m4_75) {
1680 if (count >= 4)
1681 return -EINVAL;
1682 cfg->m4_75 = 1;
1683 count++;
1684 }
1685 if (s15_s0 & GSM0808_SC_CFG_AMR_5_90 && !cfg->m5_90) {
1686 if (count >= 4)
1687 return -EINVAL;
1688 cfg->m5_90 = 1;
1689 count++;
1690 }
1691 if (s15_s0 & GSM0808_SC_CFG_AMR_6_70) {
1692 if (count >= 4)
1693 return -EINVAL;
1694 cfg->m6_70 = 1;
1695 count++;
1696 }
1697 if (s15_s0 & GSM0808_SC_CFG_AMR_7_40 && !cfg->m7_40) {
1698 if (count >= 4)
1699 return -EINVAL;
1700 cfg->m7_40 = 1;
1701 count++;
1702 }
1703 if (s15_s0 & GSM0808_SC_CFG_AMR_7_95) {
1704 if (count >= 4)
1705 return -EINVAL;
1706 cfg->m7_95 = 1;
1707 count++;
1708 }
1709 if (s15_s0 & GSM0808_SC_CFG_AMR_10_2) {
1710 if (count >= 4)
1711 return -EINVAL;
1712 cfg->m10_2 = 1;
1713 count++;
1714 }
1715 if (s15_s0 & GSM0808_SC_CFG_AMR_12_2 && !cfg->m12_2) {
1716 if (count >= 4)
1717 return -EINVAL;
1718 cfg->m12_2 = 1;
1719 count++;
1720 }
1721
1722 if (count == 0)
1723 return -EINVAL;
1724
1725 return 0;
Philipp Maier8515d032018-09-25 15:57:49 +02001726}
1727
Alexander Chemeris2ac8f912020-05-13 22:38:08 +03001728int gsm0808_get_cipher_reject_cause(const struct tlv_parsed *tp)
1729{
1730 return gsm0808_get_cause(tp);
1731}
1732
Neels Hofmeyra4399c82018-04-17 02:26:10 +02001733/*! Print a human readable name of the cell identifier to the char buffer.
1734 * This is useful both for struct gsm0808_cell_id and struct gsm0808_cell_id_list2.
1735 * See also gsm0808_cell_id_name() and gsm0808_cell_id_list_name().
1736 * \param[out] buf Destination buffer to write string representation to.
1737 * \param[in] buflen Amount of memory available in \a buf.
1738 * \param[in] id_discr Cell Identifier type.
1739 * \param[in] u Cell Identifer value.
1740 * \returns Like snprintf(): the amount of characters (excluding terminating nul) written,
1741 * or that would have been written if the buffer were large enough.
1742 */
1743int gsm0808_cell_id_u_name(char *buf, size_t buflen,
1744 enum CELL_IDENT id_discr, const union gsm0808_cell_id_u *u)
1745{
1746 switch (id_discr) {
1747 case CELL_IDENT_LAC:
1748 return snprintf(buf, buflen, "%u", u->lac);
1749 case CELL_IDENT_CI:
1750 return snprintf(buf, buflen, "%u", u->ci);
1751 case CELL_IDENT_LAC_AND_CI:
1752 return snprintf(buf, buflen, "%u-%u", u->lac_and_ci.lac, u->lac_and_ci.ci);
1753 case CELL_IDENT_LAI_AND_LAC:
1754 return snprintf(buf, buflen, "%s", osmo_lai_name(&u->lai_and_lac));
1755 case CELL_IDENT_WHOLE_GLOBAL:
1756 return snprintf(buf, buflen, "%s", osmo_cgi_name(&u->global));
Pau Espin Pedrolca33a712021-01-05 19:36:48 +01001757 case CELL_IDENT_WHOLE_GLOBAL_PS:
1758 return snprintf(buf, buflen, "%s", osmo_cgi_ps_name(&u->global_ps));
Pau Espin Pedrolb5551ee2022-02-16 13:09:32 +01001759 case CELL_IDENT_SAI:
1760 return snprintf(buf, buflen, "%s", osmo_sai_name(&u->sai));
Neels Hofmeyra4399c82018-04-17 02:26:10 +02001761 default:
1762 /* For CELL_IDENT_BSS and CELL_IDENT_NO_CELL, just print the discriminator.
1763 * Same for kinds we have no string representation of yet. */
1764 return snprintf(buf, buflen, "%s", gsm0808_cell_id_discr_name(id_discr));
1765 }
1766}
1767
Neels Hofmeyrd01ef752018-09-21 15:57:26 +02001768/*! Return true if the common information between the two Cell Identifiers match.
1769 * For example, if a LAC+CI is compared to LAC, return true if the LAC are the same.
1770 * Note that CELL_IDENT_NO_CELL will always return false.
1771 * Also CELL_IDENT_BSS will always return false, since this function cannot possibly
1772 * know the bounds of the BSS, so the caller must handle CELL_IDENT_BSS specially.
1773 * \param[in] discr1 Cell Identifier type.
1774 * \param[in] u1 Cell Identifier value.
1775 * \param[in] discr2 Other Cell Identifier type.
1776 * \param[in] u2 Other Cell Identifier value.
1777 * \param[in] exact_match If true, return true only if the CELL_IDENT types and all values are identical.
1778 * \returns True if the common fields of the above match.
1779 */
1780static bool gsm0808_cell_id_u_match(enum CELL_IDENT discr1, const union gsm0808_cell_id_u *u1,
1781 enum CELL_IDENT discr2, const union gsm0808_cell_id_u *u2,
1782 bool exact_match)
1783{
1784 struct osmo_cell_global_id a = {};
1785 struct osmo_cell_global_id b = {};
1786
1787 if (exact_match && discr1 != discr2)
1788 return false;
1789
1790 /* First handle the odd wildcard like CELL_IDENT kinds. We can't really match any of these. */
1791 switch (discr1) {
1792 case CELL_IDENT_NO_CELL:
1793 case CELL_IDENT_BSS:
1794 return discr1 == discr2;
1795 case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
1796 case CELL_IDENT_UTRAN_RNC:
1797 case CELL_IDENT_UTRAN_LAC_RNC:
1798 return false;
1799 default:
1800 break;
1801 }
1802 switch (discr2) {
1803 case CELL_IDENT_NO_CELL:
1804 case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
1805 case CELL_IDENT_UTRAN_RNC:
1806 case CELL_IDENT_UTRAN_LAC_RNC:
1807 case CELL_IDENT_BSS:
1808 return false;
1809 default:
1810 break;
1811 }
1812
1813 /* Enrich both sides to full CGI, then compare those. First set the *other* ID's values in case
1814 * they assign more items. For example:
1815 * u1 = LAC:42
1816 * u2 = LAC+CI:23+5
1817 * 1) a <- LAC+CI:23+5
1818 * 2) a <- LAC:42 so that a = LAC+CI:42+5
1819 * Now we can compare those two and find a mismatch. If the LAC were the same, we would get
1820 * identical LAC+CI and hence a match. */
1821
1822 cell_id_to_cgi(&a, discr2, u2);
1823 cell_id_to_cgi(&a, discr1, u1);
1824
1825 cell_id_to_cgi(&b, discr1, u1);
1826 cell_id_to_cgi(&b, discr2, u2);
1827
1828 return osmo_cgi_cmp(&a, &b) == 0;
1829}
1830
1831/*! Return true if the common information between the two Cell Identifiers match.
1832 * For example, if a LAC+CI is compared to LAC, return true if the LAC are the same.
1833 * Note that CELL_IDENT_NO_CELL will always return false.
1834 * Also CELL_IDENT_BSS will always return false, since this function cannot possibly
1835 * know the bounds of the BSS, so the caller must handle CELL_IDENT_BSS specially.
1836 * \param[in] id1 Cell Identifier.
1837 * \param[in] id2 Other Cell Identifier.
1838 * \param[in] exact_match If true, return true only if the CELL_IDENT types and all values are identical.
1839 * \returns True if the common fields of the above match.
1840 */
1841bool gsm0808_cell_ids_match(const struct gsm0808_cell_id *id1, const struct gsm0808_cell_id *id2, bool exact_match)
1842{
1843 return gsm0808_cell_id_u_match(id1->id_discr, &id1->id, id2->id_discr, &id2->id, exact_match);
1844}
1845
1846/*! Find an index in a Cell Identifier list that matches a given single Cell Identifer.
1847 * Compare \a id against each entry in \a list using gsm0808_cell_ids_match(), and return the list index
1848 * if a match is found. \a match_nr allows iterating all matches in the list. A match_nr <= 0 returns the
1849 * first match in the list, match_nr == 1 the second match, etc., and if match_nr exceeds the available
1850 * matches in the list, -1 is returned.
1851 * \param[in] id Cell Identifier to match.
1852 * \param[in] list Cell Identifier list to search in.
1853 * \param[in] match_nr Ignore this many matches.
1854 * \param[in] exact_match If true, consider as match only if the CELL_IDENT types and all values are identical.
Neels Hofmeyr8aa691f2019-02-26 02:59:37 +01001855 * \returns -1 if no match is found, list index if a match is found (i.e. rc == 0 means a match was found on the first
1856 * entry).
Neels Hofmeyrd01ef752018-09-21 15:57:26 +02001857 */
1858int gsm0808_cell_id_matches_list(const struct gsm0808_cell_id *id, const struct gsm0808_cell_id_list2 *list,
1859 unsigned int match_nr, bool exact_match)
1860{
1861 int i;
1862 for (i = 0; i < list->id_list_len; i++) {
1863 if (gsm0808_cell_id_u_match(id->id_discr, &id->id, list->id_discr, &list->id_list[i], exact_match)) {
1864 if (match_nr)
1865 match_nr--;
1866 else
1867 return i;
1868 }
1869 }
1870 return -1;
1871}
1872
Neels Hofmeyr3a504532019-02-10 22:28:27 +01001873/*! Copy information from a CGI to form a Cell Identifier of the specified kind.
1874 * \param [out] cid Compose new Cell Identifier here.
1875 * \param [in] id_discr Which kind of Cell Identifier to compose.
1876 * \param [in] cgi Cell Global Identifier to form the Cell Identifier from.
1877 */
1878void gsm0808_cell_id_from_cgi(struct gsm0808_cell_id *cid, enum CELL_IDENT id_discr,
1879 const struct osmo_cell_global_id *cgi)
1880{
1881 *cid = (struct gsm0808_cell_id){
1882 .id_discr = id_discr,
1883 };
1884
1885 switch (id_discr) {
1886 case CELL_IDENT_WHOLE_GLOBAL:
1887 cid->id.global = *cgi;
1888 return;
1889
Pau Espin Pedrol20b763d2021-02-15 16:06:22 +01001890 case CELL_IDENT_WHOLE_GLOBAL_PS:
1891 cid->id.global_ps = (struct osmo_cell_global_id_ps){
1892 .rai = {
1893 .lac = cgi->lai,
1894 },
1895 .cell_identity = cgi->cell_identity,
1896 };
1897 return;
Neels Hofmeyr3a504532019-02-10 22:28:27 +01001898 case CELL_IDENT_LAC_AND_CI:
1899 cid->id.lac_and_ci = (struct osmo_lac_and_ci_id){
1900 .lac = cgi->lai.lac,
1901 .ci = cgi->cell_identity,
1902 };
1903 return;
1904
1905 case CELL_IDENT_CI:
1906 cid->id.ci = cgi->cell_identity;
1907 return;
1908
1909 case CELL_IDENT_LAI:
1910 cid->id.lai_and_lac = cgi->lai;
1911 return;
1912
1913 case CELL_IDENT_LAC:
1914 cid->id.lac = cgi->lai.lac;
1915 return;
1916
Pau Espin Pedrolb5551ee2022-02-16 13:09:32 +01001917 case CELL_IDENT_SAI:
1918 cid->id.sai = (struct osmo_service_area_id){
1919 .lai = cgi->lai,
1920 };
1921 return;
1922
Neels Hofmeyr3a504532019-02-10 22:28:27 +01001923 case CELL_IDENT_NO_CELL:
1924 case CELL_IDENT_BSS:
1925 case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
1926 case CELL_IDENT_UTRAN_RNC:
1927 case CELL_IDENT_UTRAN_LAC_RNC:
1928 default:
1929 return;
1930 };
1931}
1932
1933/*! Overwrite parts of cgi with values from a Cell Identifier.
1934 * Place only those items given in cid into cgi, leaving other values unchanged.
1935 * \param[out] cgi Cell Global Identity to write to.
1936 * \param[in] cid Cell Identity to read from.
1937 * \return a bitmask of items that were set: OSMO_CGI_PART_PLMN | OSMO_CGI_PART_LAC | OSMO_CGI_PART_CI; 0 if nothing was
1938 * written to cgi.
1939 */
1940int gsm0808_cell_id_to_cgi(struct osmo_cell_global_id *cgi, const struct gsm0808_cell_id *cid)
1941{
1942 switch (cid->id_discr) {
1943 case CELL_IDENT_WHOLE_GLOBAL:
1944 *cgi = cid->id.global;
1945 return OSMO_CGI_PART_PLMN | OSMO_CGI_PART_LAC | OSMO_CGI_PART_CI;
1946
Pau Espin Pedrolca33a712021-01-05 19:36:48 +01001947 case CELL_IDENT_WHOLE_GLOBAL_PS:
1948 cgi->lai = cid->id.global_ps.rai.lac;
1949 cgi->cell_identity = cid->id.global_ps.cell_identity;
1950 return OSMO_CGI_PART_PLMN | OSMO_CGI_PART_LAC | OSMO_CGI_PART_CI;
1951
Neels Hofmeyr3a504532019-02-10 22:28:27 +01001952 case CELL_IDENT_LAC_AND_CI:
1953 cgi->lai.lac = cid->id.lac_and_ci.lac;
1954 cgi->cell_identity = cid->id.lac_and_ci.ci;
1955 return OSMO_CGI_PART_LAC | OSMO_CGI_PART_CI;
1956
1957 case CELL_IDENT_CI:
1958 cgi->cell_identity = cid->id.ci;
1959 return OSMO_CGI_PART_CI;
1960
1961 case CELL_IDENT_LAI:
1962 cgi->lai = cid->id.lai_and_lac;
1963 return OSMO_CGI_PART_PLMN | OSMO_CGI_PART_LAC;
1964
1965 case CELL_IDENT_LAC:
1966 cgi->lai.lac = cid->id.lac;
1967 return OSMO_CGI_PART_LAC;
1968
Pau Espin Pedrolb5551ee2022-02-16 13:09:32 +01001969 case CELL_IDENT_SAI:
1970 cgi->lai = cid->id.sai.lai;
1971 return OSMO_CGI_PART_PLMN | OSMO_CGI_PART_LAC;
1972
Neels Hofmeyr3a504532019-02-10 22:28:27 +01001973 case CELL_IDENT_NO_CELL:
1974 case CELL_IDENT_BSS:
1975 case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
1976 case CELL_IDENT_UTRAN_RNC:
1977 case CELL_IDENT_UTRAN_LAC_RNC:
1978 default:
1979 return 0;
1980 };
1981}
1982
Neels Hofmeyra4399c82018-04-17 02:26:10 +02001983/*! value_string[] for enum CELL_IDENT. */
1984const struct value_string gsm0808_cell_id_discr_names[] = {
1985 { CELL_IDENT_WHOLE_GLOBAL, "CGI" },
1986 { CELL_IDENT_LAC_AND_CI, "LAC-CI" },
1987 { CELL_IDENT_CI, "CI" },
1988 { CELL_IDENT_NO_CELL, "NO-CELL" },
1989 { CELL_IDENT_LAI_AND_LAC, "LAI" },
1990 { CELL_IDENT_LAC, "LAC" },
1991 { CELL_IDENT_BSS, "BSS" },
1992 { CELL_IDENT_UTRAN_PLMN_LAC_RNC, "UTRAN-PLMN-LAC-RNC" },
1993 { CELL_IDENT_UTRAN_RNC, "UTRAN-RNC" },
1994 { CELL_IDENT_UTRAN_LAC_RNC, "UTRAN-LAC-RNC" },
Pau Espin Pedrolb5551ee2022-02-16 13:09:32 +01001995 { CELL_IDENT_SAI, "SAI" },
Pau Espin Pedrolca33a712021-01-05 19:36:48 +01001996 { CELL_IDENT_WHOLE_GLOBAL_PS, "CGI-PS"},
Neels Hofmeyra4399c82018-04-17 02:26:10 +02001997 { 0, NULL }
1998};
1999
2000#define APPEND_THING(func, args...) do { \
2001 int remain = buflen - (pos - buf); \
2002 int l = func(pos, remain, ##args); \
2003 if (l < 0 || l > remain) \
2004 pos = buf + buflen; \
2005 else \
2006 pos += l; \
2007 if (l > 0) \
2008 total_len += l; \
2009 } while(0)
2010#define APPEND_STR(fmt, args...) APPEND_THING(snprintf, fmt, ##args)
2011#define APPEND_CELL_ID_U(DISCR, U) APPEND_THING(gsm0808_cell_id_u_name, DISCR, U)
2012
Harald Welte179f3572019-03-18 18:38:47 +01002013char *gsm0808_cell_id_name_buf(char *buf, size_t buflen, const struct gsm0808_cell_id *cid)
Neels Hofmeyra4399c82018-04-17 02:26:10 +02002014{
2015 char *pos = buf;
2016 int total_len = 0;
2017 APPEND_STR("%s:", gsm0808_cell_id_discr_name(cid->id_discr));
2018 APPEND_CELL_ID_U(cid->id_discr, &cid->id);
2019 return buf;
2020}
2021
2022/*! Return a human readable representation of a Cell Identifier, like "LAC:123"
2023 * or "CGI:001-01-42-23".
2024 * \param[in] cid Cell Identifer.
2025 * \returns String in a static buffer.
2026 */
2027const char *gsm0808_cell_id_name(const struct gsm0808_cell_id *cid)
2028{
Harald Welte171ef822019-03-28 10:49:05 +01002029 static __thread char buf[64];
Harald Welte179f3572019-03-18 18:38:47 +01002030 return gsm0808_cell_id_name_buf(buf, sizeof(buf), cid);
Neels Hofmeyra4399c82018-04-17 02:26:10 +02002031}
2032
2033/*! Like gsm0808_cell_id_name() but uses a different static buffer.
2034 * \param[in] cid Cell Identifer.
2035 * \returns String in a static buffer.
2036 */
2037const char *gsm0808_cell_id_name2(const struct gsm0808_cell_id *cid)
2038{
Harald Welte171ef822019-03-28 10:49:05 +01002039 static __thread char buf[64];
Harald Welte179f3572019-03-18 18:38:47 +01002040 return gsm0808_cell_id_name_buf(buf, sizeof(buf), cid);
2041}
2042
2043char *gsm0808_cell_id_name_c(const void *ctx, const struct gsm0808_cell_id *cid)
2044{
2045 char *buf = talloc_size(ctx, 64);
2046 if (!buf)
2047 return NULL;
2048 return gsm0808_cell_id_name_buf(buf, 64, cid);
Neels Hofmeyra4399c82018-04-17 02:26:10 +02002049}
2050
2051/*! Return a human readable representation of the Cell Identifier List, like
2052 * "LAC[2]:{123, 456}".
2053 * The return value semantics are like snprintf() and thus allow ensuring a complete
2054 * untruncated string by determining the required string length from the return value.
2055 * If buflen > 0, always nul-terminate the string in buf, also when it is truncated.
2056 * If buflen == 0, do not modify buf, just return the would-be length.
2057 * \param[out] buf Destination buffer to write string representation to.
2058 * \param[in] buflen Amount of memory available in \a buf.
2059 * \param[in] cil Cell Identifer List.
2060 * \returns Like snprintf(): the amount of characters (excluding terminating nul) written,
2061 * or that would have been written if the buffer were large enough.
2062 */
2063int gsm0808_cell_id_list_name_buf(char *buf, size_t buflen, const struct gsm0808_cell_id_list2 *cil)
2064{
2065 char *pos = buf;
2066 int total_len = 0;
2067 int i;
2068
2069 APPEND_STR("%s[%u]", gsm0808_cell_id_discr_name(cil->id_discr), cil->id_list_len);
2070
2071 switch (cil->id_discr) {
2072 case CELL_IDENT_BSS:
2073 case CELL_IDENT_NO_CELL:
2074 return total_len;
2075 default:
2076 break;
2077 }
2078
2079 APPEND_STR(":{");
2080
2081 for (i = 0; i < cil->id_list_len; i++) {
2082 if (i)
2083 APPEND_STR(", ");
2084 APPEND_CELL_ID_U(cil->id_discr, &cil->id_list[i]);
2085 }
2086
2087 APPEND_STR("}");
2088 return total_len;
2089}
2090
2091/*! Return a human-readable representation of \a cil in a static buffer.
2092 * If the list is too long, the output may be truncated.
2093 * See also gsm0808_cell_id_list_name_buf(). */
2094const char *gsm0808_cell_id_list_name(const struct gsm0808_cell_id_list2 *cil)
2095{
Harald Welte171ef822019-03-28 10:49:05 +01002096 static __thread char buf[1024];
Neels Hofmeyra4399c82018-04-17 02:26:10 +02002097 gsm0808_cell_id_list_name_buf(buf, sizeof(buf), cil);
2098 return buf;
2099}
2100
Harald Welte179f3572019-03-18 18:38:47 +01002101char *gsm0808_cell_id_list_name_c(const void *ctx, const struct gsm0808_cell_id_list2 *cil)
2102{
2103 char *buf = talloc_size(ctx, 1024);
2104 if (!buf)
2105 return NULL;
2106 gsm0808_cell_id_list_name_buf(buf, 1024, cil);
2107 return buf;
2108}
2109
Neels Hofmeyra4399c82018-04-17 02:26:10 +02002110#undef APPEND_STR
2111#undef APPEND_CELL_ID_U
2112
Harald Welte4a62eda2019-03-18 18:27:00 +01002113char *gsm0808_channel_type_name_buf(char *buf, size_t buf_len, const struct gsm0808_channel_type *ct)
Neels Hofmeyrafacc2b2018-04-16 22:41:51 +02002114{
Harald Welte4a62eda2019-03-18 18:27:00 +01002115 snprintf(buf, buf_len, "ch_indctr=0x%x ch_rate_type=0x%x perm_spch=%s",
Neels Hofmeyrafacc2b2018-04-16 22:41:51 +02002116 ct->ch_indctr, ct->ch_rate_type,
2117 osmo_hexdump(ct->perm_spch, ct->perm_spch_len));
2118 return buf;
2119}
2120
Harald Welte4a62eda2019-03-18 18:27:00 +01002121const char *gsm0808_channel_type_name(const struct gsm0808_channel_type *ct)
2122{
Harald Welte171ef822019-03-28 10:49:05 +01002123 static __thread char buf[128];
Harald Welte4a62eda2019-03-18 18:27:00 +01002124 return gsm0808_channel_type_name_buf(buf, sizeof(buf), ct);
2125}
2126
Harald Welte179f3572019-03-18 18:38:47 +01002127char *gsm0808_channel_type_name_c(const void *ctx, const struct gsm0808_channel_type *ct)
2128{
2129 char *buf = talloc_size(ctx, 128);
2130 if (!buf)
2131 return NULL;
2132 return gsm0808_channel_type_name_buf(buf, 128, ct);
2133}
2134
Harald Welte96e2a002017-06-12 21:44:18 +02002135/*! @} */