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