blob: 3e90a43bb62d5957d3e0bde8a8b67c26a2172690 [file] [log] [blame]
Neels Hofmeyr02de87b2020-09-18 18:00:50 +02001/* 3GPP TS 49.031 BSSMAP-LE protocol definitions */
2/*
3 * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * Author: Neels Hofmeyr <neels@hofmeyr.de>
7 *
8 * SPDX-License-Identifier: GPL-2.0+
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
Neels Hofmeyr02de87b2020-09-18 18:00:50 +020020 */
21
22#include <string.h>
23
24#include <osmocom/core/byteswap.h>
25#include <osmocom/core/endian.h>
26#include <osmocom/core/msgb.h>
27#include <osmocom/gsm/bssmap_le.h>
28#include <osmocom/gsm/bsslap.h>
29#include <osmocom/gsm/gad.h>
30#include <osmocom/gsm/gsm48.h>
31#include <osmocom/gsm/gsm0808.h>
32
33/*! \addtogroup bssmap_le
34 * @{
35 * \file bssmap_le.c
36 * Message encoding and decoding for 3GPP TS 49.031 BSSMAP-LE.
37 */
38
39#define BSSAP_LE_MSG_SIZE BSSMAP_MSG_SIZE
40#define BSSAP_LE_MSG_HEADROOM BSSMAP_MSG_HEADROOM
41
42static const struct tlv_definition osmo_bssmap_le_tlvdef = {
43 .def = {
44 [BSSMAP_LE_IEI_LCS_QoS] = { TLV_TYPE_TLV },
45 [BSSMAP_LE_IEI_LCS_PRIORITY] = { TLV_TYPE_TLV },
46 [BSSMAP_LE_IEI_LOCATION_TYPE] = { TLV_TYPE_TLV },
47 [BSSMAP_LE_IEI_GANSS_LOCATION_TYPE] = { TLV_TYPE_TLV },
48 [BSSMAP_LE_IEI_GEO_LOCATION] = { TLV_TYPE_TLV },
49 [BSSMAP_LE_IEI_POSITIONING_DATA] = { TLV_TYPE_TLV },
50 [BSSMAP_LE_IEI_GANSS_POS_DATA] = { TLV_TYPE_TLV },
51 [BSSMAP_LE_IEI_VELOCITY_DATA] = { TLV_TYPE_TLV },
52 [BSSMAP_LE_IEI_LCS_CAUSE] = { TLV_TYPE_TLV },
53 [BSSMAP_LE_IEI_LCS_CLIENT_TYPE] = { TLV_TYPE_TLV },
54 [BSSMAP_LE_IEI_APDU] = { TLV_TYPE_TL16V },
55 [BSSMAP_LE_IEI_NET_ELEM_ID] = { TLV_TYPE_TLV },
56 [BSSMAP_LE_IEI_REQ_GPS_ASS_D] = { TLV_TYPE_TLV },
57 [BSSMAP_LE_IEI_REQ_GANSS_ASS_D] = { TLV_TYPE_TLV },
58 [BSSMAP_LE_IEI_DECIPH_KEYS] = { TLV_TYPE_TLV },
59 [BSSMAP_LE_IEI_RET_ERR_REQ] = { TLV_TYPE_TLV },
60 [BSSMAP_LE_IEI_RET_ERR_CAUSE] = { TLV_TYPE_TLV },
61 [BSSMAP_LE_IEI_SEGMENTATION] = { TLV_TYPE_TLV },
62 [BSSMAP_LE_IEI_CLASSMARK3_INFO] = { TLV_TYPE_TLV },
63 [BSSMAP_LE_IEI_CAUSE] = { TLV_TYPE_TLV },
64 [BSSMAP_LE_IEI_CELL_ID] = { TLV_TYPE_TLV },
65 [BSSMAP_LE_IEI_CHOSEN_CHAN] = { TLV_TYPE_TLV },
66 [BSSMAP_LE_IEI_IMSI] = { TLV_TYPE_TLV },
67 [BSSMAP_LE_IEI_LCS_CAPABILITY] = { TLV_TYPE_TLV },
68 [BSSMAP_LE_IEI_PKT_MEAS_REP] = { TLV_TYPE_TLV },
69 [BSSMAP_LE_IEI_CELL_ID_LIST] = { TLV_TYPE_TLV },
70 [BSSMAP_LE_IEI_IMEI] = { TLV_TYPE_TLV },
71 [BSSMAP_LE_IEI_BSS_MLAT_CAP] = { TLV_TYPE_TLV },
72 [BSSMAP_LE_IEI_CELL_INFO_LIST] = { TLV_TYPE_TLV },
73 [BSSMAP_LE_IEI_BTS_RX_ACC_LVL] = { TLV_TYPE_TLV },
74 [BSSMAP_LE_IEI_MLAT_METHOD] = { TLV_TYPE_TLV },
75 [BSSMAP_LE_IEI_MLAT_TA] = { TLV_TYPE_TLV },
76 [BSSMAP_LE_IEI_MS_SYNC_ACC] = { TLV_TYPE_TLV },
77 [BSSMAP_LE_IEI_SHORT_ID_SET] = { TLV_TYPE_TLV },
78 [BSSMAP_LE_IEI_RANDOM_ID_SET] = { TLV_TYPE_TLV },
79 [BSSMAP_LE_IEI_SHORT_BSS_ID] = { TLV_TYPE_TLV },
80 [BSSMAP_LE_IEI_RANDOM_ID] = { TLV_TYPE_TLV },
81 [BSSMAP_LE_IEI_SHORT_ID] = { TLV_TYPE_TLV },
82 [BSSMAP_LE_IEI_COVERAGE_CLASS] = { TLV_TYPE_TLV },
83 [BSSMAP_LE_IEI_MTA_ACC_SEC_RQD] = { TLV_TYPE_TLV },
84 },
85};
86
87#define DEC_ERR_NO_RETURN(RC, MSG_TYPE, IEI, CAUSE, fmt, args...) do { \
88 if (err && !*err) { \
89 *err = talloc_zero(err_ctx, struct osmo_bssmap_le_err); \
90 **err = (struct osmo_bssmap_le_err){ \
91 .rc = (RC), \
92 .msg_type = (MSG_TYPE), \
93 .iei = (IEI), \
94 .cause = (CAUSE), \
95 }; \
96 (*err)->logmsg = talloc_asprintf(*err, "Error decoding BSSMAP-LE%s%s%s%s%s: " fmt, \
97 (MSG_TYPE) >= 0 ? " " : "", \
98 (MSG_TYPE) >= 0 ? osmo_bssmap_le_msgt_name(MSG_TYPE) : "", \
99 (IEI) >= 0 ? ": " : "", \
100 (IEI) >= 0 ? osmo_bssmap_le_iei_name(IEI) : "", \
101 (IEI) >= 0 ? " IE" : "", \
102 ##args); \
103 } \
104 } while(0)
105
106#define DEC_ERR(RC, MSG_TYPE, IEI, CAUSE, fmt, args...) do { \
107 DEC_ERR_NO_RETURN(RC, MSG_TYPE, IEI, CAUSE, fmt, ##args); \
108 return RC; \
109 } while(0)
110
111#define DEC_IE_MANDATORY(MSG_TYPE, IEI, DEC_FUN, DEC_FUN_ARG) do { \
112 const struct tlv_p_entry *e; \
113 int rc; \
114 if (!(e = TLVP_GET(tp, IEI))) \
115 DEC_ERR(-EINVAL, MSG_TYPE, IEI, LCS_CAUSE_DATA_MISSING_IN_REQ, "missing mandatory IE"); \
116 rc = DEC_FUN(DEC_FUN_ARG, MSG_TYPE, IEI, err, err_ctx, e->val, e->len); \
117 if (rc) \
118 DEC_ERR(rc, MSG_TYPE, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \
119 } while (0)
120
121#define DEC_IE_OPTIONAL_FLAG(MSG_TYPE, IEI, DEC_FUN, DEC_FUN_ARG, PRESENCE_FLAG) do { \
122 const struct tlv_p_entry *e; \
123 int rc; \
124 if ((e = TLVP_GET(tp, IEI))) {\
125 rc = DEC_FUN(DEC_FUN_ARG, MSG_TYPE, IEI, err, err_ctx, e->val, e->len); \
126 if (rc) \
127 DEC_ERR(rc, MSG_TYPE, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \
128 PRESENCE_FLAG = true; \
129 } \
130 } while (0)
131
132#define DEC_IE_OPTIONAL(MSG_TYPE, IEI, DEC_FUN, DEC_FUN_ARG) do { \
133 const struct tlv_p_entry *e; \
134 int rc; \
135 if ((e = TLVP_GET(tp, IEI))) {\
136 rc = DEC_FUN(DEC_FUN_ARG, MSG_TYPE, IEI, err, err_ctx, e->val, e->len); \
137 if (rc) \
138 DEC_ERR(rc, MSG_TYPE, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \
139 } \
140 } while (0)
141
142/*! Encode full BSSMAP-LE Location Type IE, including IEI tag and length.
143 * \param[inout] msg Message buffer to append to.
144 * \param[in] location_type Values to enconde.
145 * \returns length of bytes written to the msgb.
146 */
147uint8_t osmo_bssmap_le_ie_enc_location_type(struct msgb *msg,
148 const struct bssmap_le_location_type *location_type)
149{
150 uint8_t *old_tail;
151 uint8_t *tlv_len;
152 OSMO_ASSERT(msg);
153 msgb_put_u8(msg, BSSMAP_LE_IEI_LOCATION_TYPE);
154 tlv_len = msgb_put(msg, 1);
155 old_tail = msg->tail;
156 msgb_put_u8(msg, location_type->location_information);
157
158 switch (location_type->location_information) {
159 case BSSMAP_LE_LOC_INFO_ASSIST_TARGET_MS:
160 case BSSMAP_LE_LOC_INFO_BC_DECIPHER_KEYS:
161 msgb_put_u8(msg, location_type->positioning_method);
162 break;
163 default:
164 break;
165 }
166
167 *tlv_len = (uint8_t) (msg->tail - old_tail);
168 return *tlv_len + 2;
169}
170
171/*! Decode BSSMAP-LE Location Type IE value part.
172 * \param[out] lt Buffer to write decoded values to.
173 * \param[in] elem Pointer to the value part, the V of a TLV.
174 * \param[in] len Length, the L of a TLV.
175 * \returns 0 on success, negative on error; lt is always overwritten: cleared on error, populated with values on
176 * success.
177 */
178int osmo_bssmap_le_ie_dec_location_type(struct bssmap_le_location_type *lt,
179 enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
180 struct osmo_bssmap_le_err **err, void *err_ctx,
181 const uint8_t *elem, uint8_t len)
182{
183 *lt = (struct bssmap_le_location_type){};
184
185 if (!elem || len < 1)
186 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length");
187
188 lt->location_information = elem[0];
189 switch (lt->location_information) {
190
191 case BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC:
192 if (len != 1)
193 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED,
194 "location info type 'Current Geographic': length should be 1 byte, got %u", len);
195 lt->positioning_method = BSSMAP_LE_POS_METHOD_OMITTED;
196 return 0;
197
198 case BSSMAP_LE_LOC_INFO_ASSIST_TARGET_MS:
199 case BSSMAP_LE_LOC_INFO_BC_DECIPHER_KEYS:
200 if (len != 2)
201 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED,
202 "location info type %d: length should be 2 bytes, got %u",
203 lt->location_information, len);
204 lt->positioning_method = elem[1];
205 switch (lt->positioning_method) {
206 case BSSMAP_LE_POS_METHOD_MOBILE_ASSISTED_E_OTD:
207 case BSSMAP_LE_POS_METHOD_MOBILE_BASED_E_OTD:
208 case BSSMAP_LE_POS_METHOD_ASSISTED_GPS:
209 return 0;
210 default:
211 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED,
212 "location info type %d: unknown Positioning Method: %d",
213 lt->location_information, lt->positioning_method);
214 }
215
216 default:
217 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unknown location info type %d",
218 lt->location_information);
219 }
220}
221
222/*! Encode full BSSMAP-LE LCS Client Type IE, including IEI tag and length.
223 * \param[inout] msg Message buffer to append to.
224 * \param[in] client_type Value to enconde.
225 * \returns length of bytes written to the msgb.
226 */
227static uint8_t osmo_bssmap_le_ie_enc_lcs_client_type(struct msgb *msg, enum bssmap_le_lcs_client_type client_type)
228{
229 OSMO_ASSERT(msg);
230 msgb_put_u8(msg, BSSMAP_LE_IEI_LCS_CLIENT_TYPE);
231 /* length */
232 msgb_put_u8(msg, 1);
233 msgb_put_u8(msg, client_type);
234 return 3;
235}
236
237static int osmo_bssmap_le_ie_dec_lcs_client_type(enum bssmap_le_lcs_client_type *client_type,
238 enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
239 struct osmo_bssmap_le_err **err, void *err_ctx,
240 const uint8_t *elem, uint8_t len)
241{
242 *client_type = 0;
243
244 if (!elem || len < 1)
245 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length");
246
247 *client_type = elem[0];
248
249 switch (*client_type) {
250 case BSSMAP_LE_LCS_CTYPE_VALUE_ADDED_UNSPECIFIED:
251 case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_UNSPECIFIED:
252 case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_BCAST_SERVICE:
253 case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_OAM:
254 case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_ANON_STATS:
255 case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_TGT_MS_SVC:
256 case BSSMAP_LE_LCS_CTYPE_EMERG_SVC_UNSPECIFIED:
257 case BSSMAP_LE_LCS_CTYPE_LI_UNSPECIFIED:
258 return 0;
259 default:
260 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unknown LCS Client Type: %d", *client_type);
261 }
262}
263
Vadim Yanitskiybe133872022-03-22 18:17:30 +0300264/*! Encode full BSSMAP-LE LCS Priority IE, including IEI tag and length.
265 * \param[inout] msg Message buffer to append to.
266 * \param[in] priority Value to enconde.
267 * \returns length of bytes written to the msgb.
268 */
269static uint8_t osmo_bssmap_le_ie_enc_lcs_priority(struct msgb *msg, uint8_t priority)
270{
271 OSMO_ASSERT(msg);
272 msgb_put_u8(msg, BSSMAP_LE_IEI_LCS_PRIORITY);
273 /* length */
274 msgb_put_u8(msg, 1);
275 msgb_put_u8(msg, priority);
276 return 3;
277}
278
279static int osmo_bssmap_le_ie_dec_lcs_priority(uint8_t *priority,
280 enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
281 struct osmo_bssmap_le_err **err, void *err_ctx,
282 const uint8_t *elem, uint8_t len)
283{
284 if (!elem || len != 1)
285 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unexpected length");
286
287 *priority = elem[0];
288 return 0;
289}
290
291/*! Encode full BSSMAP-LE LCS QoS IE, including IEI tag and length.
292 * \param[inout] msg Message buffer to append to.
293 * \param[in] priority Value to enconde.
294 * \returns length of bytes written to the msgb.
295 */
296static uint8_t osmo_bssmap_le_ie_enc_lcs_qos(struct msgb *msg, const struct osmo_bssmap_le_lcs_qos *qos)
297{
298 OSMO_ASSERT(msg);
299 msgb_tlv_put(msg, BSSMAP_LE_IEI_LCS_QoS, sizeof(*qos), (const uint8_t *)qos);
300 return 2 + sizeof(*qos);
301}
302
303static int osmo_bssmap_le_ie_dec_lcs_qos(struct osmo_bssmap_le_lcs_qos *qos,
304 enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
305 struct osmo_bssmap_le_err **err, void *err_ctx,
306 const uint8_t *elem, uint8_t len)
307{
308 if (!elem || len != sizeof(*qos))
309 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unexpected length");
310
311 memcpy(qos, elem, len);
312 return 0;
313}
314
Neels Hofmeyr02de87b2020-09-18 18:00:50 +0200315/*! Encode the value part of 3GPP TS 49.031 10.13 LCS Cause, without IEI and len.
316 * Identically used in 3GPP TS 48.008 3.2.2.66. Usage example:
317 *
318 * uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_LCS_CAUSE);
319 * int rc = osmo_lcs_cause_enc(msg, &lcs_cause);
320 * if (rc < 0)
321 * goto error;
322 * *l = rc;
323 *
324 * \param[inout] msg Message buffer to append the LCS Cause values to.
325 * \param[in] lcs_cause LCS Cause values to enconde.
326 * \returns length of bytes written to the msgb.
327 */
328int osmo_lcs_cause_enc(struct msgb *msg, const struct lcs_cause_ie *lcs_cause)
329{
330 msgb_put_u8(msg, lcs_cause->cause_val);
331 if (lcs_cause->cause_val == LCS_CAUSE_POS_METH_FAILURE && lcs_cause->diag_val_present) {
332 msgb_put_u8(msg, lcs_cause->diag_val);
333 return 2;
334 }
335 return 1;
336}
337
338/*! Decode the value part of 3GPP TS 49.031 10.13 LCS Cause, without IEI and len.
339 * Identically used in 3GPP TS 48.008 3.2.2.66.
340 *
341 * \param[out] lcs_cause Write decoded LCS Cause values here.
342 * \param[in] data Encoded cause bytes.
343 * \param[in] len Length of data in bytes.
344 * \returns 0 on success, negative on error.
345 */
346int osmo_lcs_cause_dec(struct lcs_cause_ie *lcs_cause,
347 enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
348 struct osmo_bssmap_le_err **err, void *err_ctx,
349 const uint8_t *data, uint8_t len)
350{
351 *lcs_cause = (struct lcs_cause_ie){};
352
353 if (!data || len < 1)
354 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length");
355
356 lcs_cause->present = true;
357 lcs_cause->cause_val = data[0];
358 if (len > 1) {
359 lcs_cause->diag_val_present = true;
360 lcs_cause->diag_val = data[1];
361 }
362 if (len > 2)
363 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "expected length <= 2, got %u", len);
364
365 return 0;
366}
367
368static int osmo_bssmap_le_ie_enc_apdu(struct msgb *msg, const struct bsslap_pdu *bsslap)
369{
370 uint8_t *old_tail;
371 void *l;
372 msgb_put_u8(msg, BSSMAP_LE_IEI_APDU);
373 l = msgb_put(msg, 2);
374 old_tail = msg->tail;
375 msgb_put_u8(msg, BSSMAP_LE_APDU_PROT_BSSLAP);
376 int rc = osmo_bsslap_enc(msg, bsslap);
377 if (rc <= 0)
378 return -EINVAL;
379 osmo_store16be(msg->tail - old_tail, l);
380 return 0;
381}
382
383static int osmo_bssmap_le_ie_dec_apdu(struct bsslap_pdu *bsslap,
384 enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
385 struct osmo_bssmap_le_err **err, void *err_ctx,
386 const uint8_t *data, size_t len)
387{
388 enum bssmap_le_apdu_proto proto;
389 struct osmo_bsslap_err *bsslap_err;
390
391 if (!data || len < 1)
392 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length");
393
394 proto = data[0];
395
396 switch (proto) {
397 case BSSMAP_LE_APDU_PROT_BSSLAP:
398 if (osmo_bsslap_dec(bsslap, &bsslap_err, err_ctx, data + 1, len - 1)) {
399 DEC_ERR_NO_RETURN(bsslap_err ? bsslap_err->rc : -EINVAL,
400 msgt, iei, LCS_CAUSE_UNSPECIFIED,
401 "Error decoding BSSLAP%s%s",
402 bsslap_err && bsslap_err->logmsg ? ": " : "",
403 bsslap_err && bsslap_err->logmsg ? bsslap_err->logmsg : "");
404 (*err)->bsslap_err = bsslap_err;
405 return (*err)->rc;
406 }
407 return 0;
408 case BSSMAP_LE_APDU_PROT_LLP:
409 case BSSMAP_LE_APDU_PROT_SMLCPP:
410 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Unimplemented APDU type: %d", proto);
411 default:
412 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Invalid APDU type: %d", proto);
413 }
414}
415
416static int osmo_bssmap_le_ie_dec_cell_id(struct gsm0808_cell_id *cell_id,
417 enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
418 struct osmo_bssmap_le_err **err, void *err_ctx,
419 const uint8_t *elem, uint8_t len)
420{
421 int rc;
422 rc = gsm0808_dec_cell_id(cell_id, elem, len);
423 if (rc <= 0)
424 DEC_ERR(rc, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Error decoding Cell Identifier %s",
425 osmo_hexdump_c(err_ctx, elem, len));
426 return 0;
427}
428
429static int osmo_bssmap_le_ie_dec_imsi(struct osmo_mobile_identity *imsi,
430 enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
431 struct osmo_bssmap_le_err **err, void *err_ctx,
432 const uint8_t *elem, uint8_t len)
433{
434 int rc;
435 rc = osmo_mobile_identity_decode(imsi, elem, len, false);
436 if (rc || imsi->type != GSM_MI_TYPE_IMSI)
437 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED,
438 "cannot parse IMSI identity %s", osmo_hexdump_c(err_ctx, elem, len));
439 return 0;
440}
441
442static int osmo_bssmap_le_ie_dec_imei(struct osmo_mobile_identity *imei,
443 enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
444 struct osmo_bssmap_le_err **err, void *err_ctx,
445 const uint8_t *elem, uint8_t len)
446{
447 int rc;
448 rc = osmo_mobile_identity_decode(imei, elem, len, false);
449 if (rc || imei->type != GSM_MI_TYPE_IMEI)
450 DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED,
451 "cannot parse IMEI identity %s", osmo_hexdump_c(err_ctx, elem, len));
452 return 0;
453}
454
455static int osmo_bssmap_le_ie_dec_gad(union gad_raw *gad,
456 enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
457 struct osmo_bssmap_le_err **err, void *err_ctx,
458 const uint8_t *elem, uint8_t len)
459{
460 struct osmo_gad_err *gad_err;
461 if (osmo_gad_raw_read(gad, &gad_err, err_ctx, elem, len)) {
462 DEC_ERR_NO_RETURN(gad_err ? gad_err->rc : -EINVAL,
463 msgt, BSSMAP_LE_IEI_GEO_LOCATION, LCS_CAUSE_UNSPECIFIED,
464 "Error decoding GAD%s%s",
465 gad_err && gad_err->logmsg ? ": " : "",
466 gad_err && gad_err->logmsg ? gad_err->logmsg : "");
467 (*err)->gad_err = gad_err;
468 return (*err)->rc;
469 }
470 return 0;
471}
472
473struct osmo_bssap_le_header {
474 uint8_t type;
475 uint8_t length;
476 uint8_t data[0];
477} __attribute__((packed));
478
479/*! Return the BSSMAP-LE msg_type from a BSSAP-LE PDU, e.g. from a msgb_l3().
480 * \param[in] data BSSAP-LE PDU data, starting with BSSAP-LE discriminator.
481 * \param[in] len Length of data in bytes.
482 * \returns bssmap_le_msgt or negative on error or non-BSSMAP-LE discriminator. */
483enum bssmap_le_msgt osmo_bssmap_le_msgt(const uint8_t *data, uint8_t len)
484{
485 const struct osmo_bssap_le_header *h = (void*)data;
486 if (!data || len < sizeof(struct osmo_bssap_le_header) + 1)
487 return -1;
488 if (h->type != BSSAP_LE_MSG_DISCR_BSSMAP_LE)
489 return -1;
490 return h->data[0];
491}
492
493static int osmo_bssmap_le_enc_reset(struct msgb *msg, enum gsm0808_cause cause)
494{
495 /* The BSSMAP-LE Reset Cause is defined as identical to the 3GPP TS 48.008 Cause. */
496 gsm0808_enc_cause(msg, cause);
497 return 0;
498}
499
500static int osmo_bssmap_le_dec_reset(enum gsm0808_cause *cause,
501 enum bssmap_le_msgt msgt,
502 struct osmo_bssmap_le_err **err, void *err_ctx,
503 const struct tlv_parsed *tp)
504{
505 const struct tlv_p_entry *e;
506
507 if (!(e = TLVP_GET(tp, BSSMAP_LE_IEI_CAUSE)))
508 DEC_ERR(-EINVAL, msgt, BSSMAP_LE_IEI_CAUSE, LCS_CAUSE_DATA_MISSING_IN_REQ, "missing mandatory IE");
509
510 *cause = gsm0808_get_cause(tp);
511 if (*cause < 0)
512 DEC_ERR(-EINVAL, msgt, BSSMAP_LE_IEI_CAUSE, LCS_CAUSE_UNSPECIFIED, "cannot parse IE");
513
514 return 0;
515}
516
517static int osmo_bssmap_le_enc_perform_loc_req(struct msgb *msg, const struct bssmap_le_perform_loc_req *params)
518{
519 osmo_bssmap_le_ie_enc_location_type(msg, &params->location_type);
520
521 gsm0808_enc_cell_id(msg, &params->cell_id);
522
523 if (params->lcs_client_type_present)
524 osmo_bssmap_le_ie_enc_lcs_client_type(msg, params->lcs_client_type);
525
Vadim Yanitskiybe133872022-03-22 18:17:30 +0300526 if (params->more_items && params->lcs_priority_present)
527 osmo_bssmap_le_ie_enc_lcs_priority(msg, params->lcs_priority);
528
529 if (params->more_items && params->lcs_qos_present)
530 osmo_bssmap_le_ie_enc_lcs_qos(msg, &params->lcs_qos);
531
Neels Hofmeyr02de87b2020-09-18 18:00:50 +0200532 if (params->apdu_present) {
533 int rc = osmo_bssmap_le_ie_enc_apdu(msg, &params->apdu);
534 if (rc < 0)
535 return rc;
536 }
537
538 if (params->imsi.type == GSM_MI_TYPE_IMSI) {
539 uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_IMSI);
540 int rc = osmo_mobile_identity_encode_msgb(msg, &params->imsi, false);
541 if (rc < 0)
542 return rc;
543 *l = rc;
544 }
545
546 if (params->imei.type == GSM_MI_TYPE_IMEI) {
547 uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_IMEI);
548 int rc = osmo_mobile_identity_encode_msgb(msg, &params->imei, false);
549 if (rc < 0)
550 return rc;
551 *l = rc;
552 }
553 return 0;
554}
555
556static int osmo_bssmap_le_dec_perform_loc_req(struct bssmap_le_perform_loc_req *params,
557 enum bssmap_le_msgt msgt,
558 struct osmo_bssmap_le_err **err, void *err_ctx,
559 const struct tlv_parsed *tp)
560{
561 *params = (struct bssmap_le_perform_loc_req){};
562
563 DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_LOCATION_TYPE, osmo_bssmap_le_ie_dec_location_type,
564 &params->location_type);
565 DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_CELL_ID, osmo_bssmap_le_ie_dec_cell_id,
566 &params->cell_id);
567 DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_CLIENT_TYPE, osmo_bssmap_le_ie_dec_lcs_client_type,
568 &params->lcs_client_type, params->lcs_client_type_present);
Vadim Yanitskiybe133872022-03-22 18:17:30 +0300569 DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_PRIORITY, osmo_bssmap_le_ie_dec_lcs_priority,
570 &params->lcs_priority, params->lcs_priority_present);
571 DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_QoS, osmo_bssmap_le_ie_dec_lcs_qos,
572 &params->lcs_qos, params->lcs_qos_present);
Neels Hofmeyr02de87b2020-09-18 18:00:50 +0200573 DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_APDU, osmo_bssmap_le_ie_dec_apdu, &params->apdu,
574 params->apdu_present);
575 DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_IMSI, osmo_bssmap_le_ie_dec_imsi, &params->imsi);
576 DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_IMEI, osmo_bssmap_le_ie_dec_imei, &params->imei);
577
Vadim Yanitskiybe133872022-03-22 18:17:30 +0300578 if (params->lcs_priority_present || params->lcs_qos_present)
579 params->more_items = true;
580
Neels Hofmeyr02de87b2020-09-18 18:00:50 +0200581 return 0;
582}
583
584static int osmo_bssmap_le_enc_perform_loc_resp(struct msgb *msg, const struct bssmap_le_perform_loc_resp *params)
585{
586 if (params->location_estimate_present) {
587 uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_GEO_LOCATION);
588 int rc = osmo_gad_raw_write(msg, &params->location_estimate);
589 if (rc < 0)
590 return rc;
591 *l = rc;
592 }
593
594 if (params->lcs_cause.present) {
595 uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_LCS_CAUSE);
596 int rc = osmo_lcs_cause_enc(msg, &params->lcs_cause);
597 if (rc < 0)
598 return rc;
599 *l = rc;
600 }
601 return 0;
602}
603
604static int osmo_bssmap_le_dec_perform_loc_resp(struct bssmap_le_perform_loc_resp *params,
605 enum bssmap_le_msgt msgt,
606 struct osmo_bssmap_le_err **err, void *err_ctx,
607 const struct tlv_parsed *tp)
608{
609 *params = (struct bssmap_le_perform_loc_resp){};
610
611 DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_GEO_LOCATION, osmo_bssmap_le_ie_dec_gad, &params->location_estimate,
612 params->location_estimate_present);
613 DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_LCS_CAUSE, osmo_lcs_cause_dec, &params->lcs_cause);
614
615 return 0;
616}
617
618static int osmo_bssmap_le_enc_perform_loc_abort(struct msgb *msg, const struct lcs_cause_ie *params)
619{
620 uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_LCS_CAUSE);
621 int rc = osmo_lcs_cause_enc(msg, params);
622 if (rc < 0)
623 return rc;
624 *l = rc;
625 return 0;
626}
627
628static int osmo_bssmap_le_dec_perform_loc_abort(struct lcs_cause_ie *params,
629 enum bssmap_le_msgt msgt,
630 struct osmo_bssmap_le_err **err, void *err_ctx,
631 const struct tlv_parsed *tp)
632{
633 *params = (struct lcs_cause_ie){};
634
635 DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_LCS_CAUSE, osmo_lcs_cause_dec, params);
636 return 0;
637}
638
639static int osmo_bssmap_le_enc_conn_oriented_info(struct msgb *msg,
640 const struct bssmap_le_conn_oriented_info *params)
641{
642 return osmo_bssmap_le_ie_enc_apdu(msg, &params->apdu);
643}
644
645static int osmo_bssmap_le_dec_conn_oriented_info(struct bssmap_le_conn_oriented_info *params,
646 enum bssmap_le_msgt msgt,
647 struct osmo_bssmap_le_err **err, void *err_ctx,
648 const struct tlv_parsed *tp)
649{
650 *params = (struct bssmap_le_conn_oriented_info){};
651 DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_APDU, osmo_bssmap_le_ie_dec_apdu, &params->apdu);
652 return 0;
653}
654
655/*! Encode BSSMAP-LE PDU and add to msgb (3GPP TS 49.031).
656 * See also osmo_bssap_le_enc().
657 * \param[out] msg msgb to append to.
658 * \param[in] pdu PDU data to encode.
659 * \return number of bytes written, negative on error.
660 */
661static int osmo_bssmap_le_enc(struct msgb *msg, const struct bssmap_le_pdu *pdu)
662{
663 int rc;
664 uint8_t *old_tail;
665 old_tail = msg->tail;
666
667 msgb_v_put(msg, pdu->msg_type);
668
669 switch (pdu->msg_type) {
670 case BSSMAP_LE_MSGT_RESET:
671 rc = osmo_bssmap_le_enc_reset(msg, pdu->reset);
672 break;
673 case BSSMAP_LE_MSGT_RESET_ACK:
674 /* Consists only of the message type. */
675 rc = 0;
676 break;
677 case BSSMAP_LE_MSGT_PERFORM_LOC_REQ:
678 rc = osmo_bssmap_le_enc_perform_loc_req(msg, &pdu->perform_loc_req);
679 break;
680 case BSSMAP_LE_MSGT_PERFORM_LOC_RESP:
681 rc = osmo_bssmap_le_enc_perform_loc_resp(msg, &pdu->perform_loc_resp);
682 break;
683 case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT:
684 rc = osmo_bssmap_le_enc_perform_loc_abort(msg, &pdu->perform_loc_abort);
685 break;
686 case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:
687 rc = osmo_bssmap_le_enc_conn_oriented_info(msg, &pdu->conn_oriented_info);
688 break;
689 default:
690 rc = -ENOTSUP;
691 }
692
693 if (rc < 0)
694 return rc;
695
696 return (msg->tail - old_tail);
697}
698
699/*! Decode BSSMAP-LE PDU (3GPP TS 49.031).
700 * See also osmo_bssap_le_dec().
701 * \param[out] pdu Write decoded values here.
702 * \param[in] data Pointer to BSSMAP-LE PDU raw data.
703 * \param[in] len Data length to decode.
704 * \return NULL upon success, a human readable error message on failure.
705 */
706static int osmo_bssmap_le_dec(struct bssmap_le_pdu *pdu,
707 struct osmo_bssmap_le_err **err, void *err_ctx,
708 const uint8_t *data, size_t len)
709{
710 const uint8_t *ies_start;
711 int ies_len;
712 struct tlv_parsed tp;
713
714 *pdu = (struct bssmap_le_pdu){};
715
716 if (len < 1)
717 DEC_ERR(-EINVAL, -1, -1, LCS_CAUSE_UNSPECIFIED, "zero length");
718 pdu->msg_type = data[0];
719
720 /* BSSMAP-LE IEs */
721 ies_start = &data[1];
722 ies_len = len - 1;
723
724 if (tlv_parse(&tp, &osmo_bssmap_le_tlvdef, ies_start, ies_len, 0, 0) < 0)
725 DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "failed to parse TLV structure");
726
727 switch (pdu->msg_type) {
728 case BSSMAP_LE_MSGT_RESET:
729 return osmo_bssmap_le_dec_reset(&pdu->reset, pdu->msg_type, err, err_ctx, &tp);
730 case BSSMAP_LE_MSGT_RESET_ACK:
731 /* Consists only of the message type. */
732 return 0;
733 case BSSMAP_LE_MSGT_PERFORM_LOC_REQ:
734 return osmo_bssmap_le_dec_perform_loc_req(&pdu->perform_loc_req, pdu->msg_type, err, err_ctx, &tp);
735 case BSSMAP_LE_MSGT_PERFORM_LOC_RESP:
736 return osmo_bssmap_le_dec_perform_loc_resp(&pdu->perform_loc_resp, pdu->msg_type, err, err_ctx, &tp);
737 case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT:
738 return osmo_bssmap_le_dec_perform_loc_abort(&pdu->perform_loc_abort, pdu->msg_type, err, err_ctx, &tp);
739 case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:
740 return osmo_bssmap_le_dec_conn_oriented_info(&pdu->conn_oriented_info, pdu->msg_type, err, err_ctx,
741 &tp);
742 default:
743 DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "Unsupported BSSMAP-LE message type");
744 }
745}
746
747/*! Encode BSSAP-LE PDU returned in new msgb (3GPP TS 49.031).
748 * By spec, BSSAP-LE contains either BSSMAP-LE or DTAP.
749 * \param[in] pdu PDU data to encode.
750 * \return msgb with encoded data and l2h set to the start.
751 */
752struct msgb *osmo_bssap_le_enc(const struct bssap_le_pdu *pdu)
753{
754 struct msgb *msg;
755 int rc;
756
757 if (pdu->discr != BSSAP_LE_MSG_DISCR_BSSMAP_LE)
758 return NULL;
759
760 msg = msgb_alloc_headroom(BSSAP_LE_MSG_SIZE, BSSAP_LE_MSG_HEADROOM,
761 osmo_bssmap_le_msgt_name(pdu->bssmap_le.msg_type));
762 if (!msg)
763 return NULL;
764
765 rc = osmo_bssmap_le_enc(msg, &pdu->bssmap_le);
766 if (rc <= 0) {
767 msgb_free(msg);
768 return NULL;
769 }
770
771 /* prepend header with final length */
772 msg->l2h = msgb_tv_push(msg, pdu->discr, msgb_length(msg));
773
774 return msg;
775}
776
777/*! Decode BSSAP-LE PDU (3GPP TS 49.031).
778 * \param[out] pdu Write decoded values here.
779 * \param[in] data Pointer to BSSMAP-LE PDU raw data.
780 * \param[in] len Data length to decode.
781 * \return NULL upon success, a human readable error message on failure.
782 */
783int osmo_bssap_le_dec(struct bssap_le_pdu *pdu, struct osmo_bssap_le_err **err, void *err_ctx, struct msgb *msg)
784{
785 struct osmo_bssap_le_header *h;
786 unsigned int check_len;
787 struct osmo_bssmap_le_err *bssmap_le_err = NULL;
788 int rc;
789
790#define BSSAP_LE_DEC_ERR(RC, fmt, args...) do { \
791 if (err && !*err) { \
792 *err = talloc_zero(err_ctx, struct osmo_bssap_le_err); \
793 **err = (struct osmo_bssap_le_err){ \
794 .rc = (RC), \
795 .logmsg = talloc_asprintf(*err, "Error decoding BSSAP-LE: " fmt, ##args), \
796 }; \
797 } \
798 return RC; \
799 } while(0)
800
801 *pdu = (struct bssap_le_pdu){};
802
803 h = msgb_l2(msg);
804 if (!h)
805 BSSAP_LE_DEC_ERR(-EINVAL, "missing msgb_l2() pointer");
806 if (msgb_l2len(msg) < sizeof(*h))
807 BSSAP_LE_DEC_ERR(-EINVAL, "message too short for header");
808 check_len = msgb_l2len(msg) - sizeof(*h);
809 if (h->length < check_len)
810 BSSAP_LE_DEC_ERR(-EINVAL, "message truncated, header length (%u) longer than message (%u)",
811 h->length, check_len);
812
813 switch (h->type) {
814 case BSSAP_LE_MSG_DISCR_BSSMAP_LE:
815 break;
816 default:
817 BSSAP_LE_DEC_ERR(-EINVAL, "unsupported discr %u, only BSSMAP-LE is implemented", h->type);
818 }
819
820 rc = osmo_bssmap_le_dec(&pdu->bssmap_le, err ? &bssmap_le_err : NULL, err_ctx,
821 h->data, h->length);
822 if (rc)
823 BSSAP_LE_DEC_ERR(rc, "%s",
824 (bssmap_le_err && bssmap_le_err->logmsg) ?
825 bssmap_le_err->logmsg : "unknown error in BSSMAP-LE part");
826 return 0;
827}
828
829const struct value_string osmo_bssmap_le_msgt_names[] = {
830 { BSSMAP_LE_MSGT_PERFORM_LOC_REQ, "PERFORM LOCATION REQUEST" },
831 { BSSMAP_LE_MSGT_PERFORM_LOC_RESP, "PERFORM LOCATION RESPONSE" },
832 { BSSMAP_LE_MSGT_PERFORM_LOC_ABORT, "PERFORM LOCATION ABORT" },
833 { BSSMAP_LE_MSGT_PERFORM_LOC_INFO, "PERFORM LOCATION INFO" },
834 { BSSMAP_LE_MSGT_ASSIST_INFO_REQ, "ASSISTANCE INFORMATION REQUEST" },
835 { BSSMAP_LE_MSGT_ASSIST_INFO_RESP, "ASSISTANCE INFORMATION RESPONSE" },
836 { BSSMAP_LE_MSGT_CONN_ORIENTED_INFO, "CONNECTION ORIENTED INFORMATON" },
837 { BSSMAP_LE_MSGT_CONN_LESS_INFO, "CONNECTIONLESS INFORMATION" },
838 { BSSMAP_LE_MSGT_RESET, "RESET" },
839 { BSSMAP_LE_MSGT_RESET_ACK, "RESET ACKNOWLEDGE" },
840 {}
841};
842
843const struct value_string osmo_bssmap_le_iei_names[] = {
844 { BSSMAP_LE_IEI_LCS_QoS, "LCS_QoS" },
845 { BSSMAP_LE_IEI_LCS_PRIORITY, "LCS_PRIORITY" },
846 { BSSMAP_LE_IEI_LOCATION_TYPE, "LOCATION_TYPE" },
847 { BSSMAP_LE_IEI_GANSS_LOCATION_TYPE, "GANSS_LOCATION_TYPE" },
848 { BSSMAP_LE_IEI_GEO_LOCATION, "GEO_LOCATION" },
849 { BSSMAP_LE_IEI_POSITIONING_DATA, "POSITIONING_DATA" },
850 { BSSMAP_LE_IEI_GANSS_POS_DATA, "GANSS_POS_DATA" },
851 { BSSMAP_LE_IEI_VELOCITY_DATA, "VELOCITY_DATA" },
852 { BSSMAP_LE_IEI_LCS_CAUSE, "LCS_CAUSE" },
853 { BSSMAP_LE_IEI_LCS_CLIENT_TYPE, "LCS_CLIENT_TYPE" },
854 { BSSMAP_LE_IEI_APDU, "APDU" },
855 { BSSMAP_LE_IEI_NET_ELEM_ID, "NET_ELEM_ID" },
856 { BSSMAP_LE_IEI_REQ_GPS_ASS_D, "REQ_GPS_ASS_D" },
857 { BSSMAP_LE_IEI_REQ_GANSS_ASS_D, "REQ_GANSS_ASS_D" },
858 { BSSMAP_LE_IEI_DECIPH_KEYS, "DECIPH_KEYS" },
859 { BSSMAP_LE_IEI_RET_ERR_REQ, "RET_ERR_REQ" },
860 { BSSMAP_LE_IEI_RET_ERR_CAUSE, "RET_ERR_CAUSE" },
861 { BSSMAP_LE_IEI_SEGMENTATION, "SEGMENTATION" },
862 { BSSMAP_LE_IEI_CLASSMARK3_INFO, "CLASSMARK3_INFO" },
863 { BSSMAP_LE_IEI_CAUSE, "CAUSE" },
864 { BSSMAP_LE_IEI_CELL_ID, "CELL_ID" },
865 { BSSMAP_LE_IEI_CHOSEN_CHAN, "CHOSEN_CHAN" },
866 { BSSMAP_LE_IEI_IMSI, "IMSI" },
867 { BSSMAP_LE_IEI_LCS_CAPABILITY, "LCS_CAPABILITY" },
868 { BSSMAP_LE_IEI_PKT_MEAS_REP, "PKT_MEAS_REP" },
869 { BSSMAP_LE_IEI_CELL_ID_LIST, "CELL_ID_LIST" },
870 { BSSMAP_LE_IEI_IMEI, "IMEI" },
871 { BSSMAP_LE_IEI_BSS_MLAT_CAP, "BSS_MLAT_CAP" },
872 { BSSMAP_LE_IEI_CELL_INFO_LIST, "CELL_INFO_LIST" },
873 { BSSMAP_LE_IEI_BTS_RX_ACC_LVL, "BTS_RX_ACC_LVL" },
874 { BSSMAP_LE_IEI_MLAT_METHOD, "MLAT_METHOD" },
875 { BSSMAP_LE_IEI_MLAT_TA, "MLAT_TA" },
876 { BSSMAP_LE_IEI_MS_SYNC_ACC, "MS_SYNC_ACC" },
877 { BSSMAP_LE_IEI_SHORT_ID_SET, "SHORT_ID_SET" },
878 { BSSMAP_LE_IEI_RANDOM_ID_SET, "RANDOM_ID_SET" },
879 { BSSMAP_LE_IEI_SHORT_BSS_ID, "SHORT_BSS_ID" },
880 { BSSMAP_LE_IEI_RANDOM_ID, "RANDOM_ID" },
881 { BSSMAP_LE_IEI_SHORT_ID, "SHORT_ID" },
882 { BSSMAP_LE_IEI_COVERAGE_CLASS, "COVERAGE_CLASS" },
883 { BSSMAP_LE_IEI_MTA_ACC_SEC_RQD, "MTA_ACC_SEC_RQD" },
884 {}
885};
886
887/*! Return a human readable string describing a BSSAP-LE PDU.
888 * \param[out] buf String buffer to write to.
889 * \param[in] buflen sizeof(buf).
890 * \param[in] bssap_le Decoded BSSAP-LE PDU data.
891 * \returns number of chars that would be written, like snprintf().
892 */
893int osmo_bssap_le_pdu_to_str_buf(char *buf, size_t buflen, const struct bssap_le_pdu *bssap_le)
894{
895 struct osmo_strbuf sb = { .buf = buf, .len = buflen };
896 const struct bssmap_le_pdu *bssmap_le;
897
898 switch (bssap_le->discr) {
899 case BSSAP_LE_MSG_DISCR_BSSMAP_LE:
900 bssmap_le = &bssap_le->bssmap_le;
901 OSMO_STRBUF_PRINTF(sb, "BSSMAP-LE %s", osmo_bssmap_le_msgt_name(bssmap_le->msg_type));
902 switch (bssmap_le->msg_type) {
903 case BSSMAP_LE_MSGT_PERFORM_LOC_REQ:
904 if (bssmap_le->perform_loc_req.apdu_present)
905 OSMO_STRBUF_PRINTF(sb, " with BSSLAP %s",
906 osmo_bsslap_msgt_name(bssmap_le->perform_loc_req.apdu.msg_type));
907 break;
908
909 case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:
910 OSMO_STRBUF_PRINTF(sb, " with BSSLAP %s",
911 osmo_bsslap_msgt_name(bssmap_le->conn_oriented_info.apdu.msg_type));
912 break;
913
914 default:
915 break;
916 }
917 break;
918 default:
919 OSMO_STRBUF_PRINTF(sb, "BSSAP-LE discr %d not implemented", bssap_le->discr);
920 break;
921 }
922
923 return sb.chars_needed;
924}
925
926/*! Return a human readable string describing a BSSAP-LE PDU.
927 * \param[in] ctx Talloc context to allocate string buffer from.
928 * \param[in] bssap_le Decoded BSSAP-LE PDU data.
929 * \returns string.
930 */
931char *osmo_bssap_le_pdu_to_str_c(void *ctx, const struct bssap_le_pdu *bssap_le)
932{
933 OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_bssap_le_pdu_to_str_buf, bssap_le)
934}
935
936/*! @} */