Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 1 | /* 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 Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 20 | */ |
| 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 | |
| 42 | static 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 | */ |
| 147 | uint8_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 | */ |
| 178 | int 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 | { |
Vadim Yanitskiy | 7b9b307 | 2023-02-25 05:52:37 +0700 | [diff] [blame] | 183 | memset(lt, 0x00, sizeof(*lt)); |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 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 | */ |
| 227 | static 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 | |
| 237 | static 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 Yanitskiy | be13387 | 2022-03-22 18:17:30 +0300 | [diff] [blame] | 264 | /*! 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 | */ |
| 269 | static 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 | |
| 279 | static 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 | */ |
| 296 | static 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 | |
| 303 | static 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 Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 315 | /*! 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 | */ |
| 328 | int 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 | */ |
| 346 | int 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 | { |
Vadim Yanitskiy | 7b9b307 | 2023-02-25 05:52:37 +0700 | [diff] [blame] | 351 | memset(lcs_cause, 0x00, sizeof(*lcs_cause)); |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 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 | |
| 368 | static 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 | |
| 383 | static 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 | |
| 416 | static 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 | |
| 429 | static 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 | |
| 442 | static 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 | |
| 455 | static 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 | |
| 473 | struct 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. */ |
| 483 | enum 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 | |
| 493 | static 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 | |
| 500 | static 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 | |
| 517 | static 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, ¶ms->location_type); |
| 520 | |
| 521 | gsm0808_enc_cell_id(msg, ¶ms->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 Yanitskiy | be13387 | 2022-03-22 18:17:30 +0300 | [diff] [blame] | 526 | 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, ¶ms->lcs_qos); |
| 531 | |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 532 | if (params->apdu_present) { |
| 533 | int rc = osmo_bssmap_le_ie_enc_apdu(msg, ¶ms->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, ¶ms->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, ¶ms->imei, false); |
| 549 | if (rc < 0) |
| 550 | return rc; |
| 551 | *l = rc; |
| 552 | } |
| 553 | return 0; |
| 554 | } |
| 555 | |
| 556 | static 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 | { |
Vadim Yanitskiy | 7b9b307 | 2023-02-25 05:52:37 +0700 | [diff] [blame] | 561 | memset(params, 0x00, sizeof(*params)); |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 562 | |
| 563 | DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_LOCATION_TYPE, osmo_bssmap_le_ie_dec_location_type, |
| 564 | ¶ms->location_type); |
| 565 | DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_CELL_ID, osmo_bssmap_le_ie_dec_cell_id, |
| 566 | ¶ms->cell_id); |
| 567 | DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_CLIENT_TYPE, osmo_bssmap_le_ie_dec_lcs_client_type, |
| 568 | ¶ms->lcs_client_type, params->lcs_client_type_present); |
Vadim Yanitskiy | be13387 | 2022-03-22 18:17:30 +0300 | [diff] [blame] | 569 | DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_PRIORITY, osmo_bssmap_le_ie_dec_lcs_priority, |
| 570 | ¶ms->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 | ¶ms->lcs_qos, params->lcs_qos_present); |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 573 | DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_APDU, osmo_bssmap_le_ie_dec_apdu, ¶ms->apdu, |
| 574 | params->apdu_present); |
| 575 | DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_IMSI, osmo_bssmap_le_ie_dec_imsi, ¶ms->imsi); |
| 576 | DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_IMEI, osmo_bssmap_le_ie_dec_imei, ¶ms->imei); |
| 577 | |
Vadim Yanitskiy | be13387 | 2022-03-22 18:17:30 +0300 | [diff] [blame] | 578 | if (params->lcs_priority_present || params->lcs_qos_present) |
| 579 | params->more_items = true; |
| 580 | |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 581 | return 0; |
| 582 | } |
| 583 | |
| 584 | static 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, ¶ms->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, ¶ms->lcs_cause); |
| 597 | if (rc < 0) |
| 598 | return rc; |
| 599 | *l = rc; |
| 600 | } |
| 601 | return 0; |
| 602 | } |
| 603 | |
| 604 | static 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 | { |
Vadim Yanitskiy | 7b9b307 | 2023-02-25 05:52:37 +0700 | [diff] [blame] | 609 | memset(params, 0x00, sizeof(*params)); |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 610 | |
| 611 | DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_GEO_LOCATION, osmo_bssmap_le_ie_dec_gad, ¶ms->location_estimate, |
| 612 | params->location_estimate_present); |
| 613 | DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_LCS_CAUSE, osmo_lcs_cause_dec, ¶ms->lcs_cause); |
| 614 | |
| 615 | return 0; |
| 616 | } |
| 617 | |
| 618 | static 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 | |
| 628 | static 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 | { |
Vadim Yanitskiy | 7b9b307 | 2023-02-25 05:52:37 +0700 | [diff] [blame] | 633 | memset(params, 0x00, sizeof(*params)); |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 634 | |
| 635 | DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_LCS_CAUSE, osmo_lcs_cause_dec, params); |
| 636 | return 0; |
| 637 | } |
| 638 | |
| 639 | static 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, ¶ms->apdu); |
| 643 | } |
| 644 | |
| 645 | static 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 | { |
Vadim Yanitskiy | 7b9b307 | 2023-02-25 05:52:37 +0700 | [diff] [blame] | 650 | memset(params, 0x00, sizeof(*params)); |
| 651 | |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 652 | DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_APDU, osmo_bssmap_le_ie_dec_apdu, ¶ms->apdu); |
| 653 | return 0; |
| 654 | } |
| 655 | |
| 656 | /*! Encode BSSMAP-LE PDU and add to msgb (3GPP TS 49.031). |
| 657 | * See also osmo_bssap_le_enc(). |
| 658 | * \param[out] msg msgb to append to. |
| 659 | * \param[in] pdu PDU data to encode. |
| 660 | * \return number of bytes written, negative on error. |
| 661 | */ |
| 662 | static int osmo_bssmap_le_enc(struct msgb *msg, const struct bssmap_le_pdu *pdu) |
| 663 | { |
| 664 | int rc; |
| 665 | uint8_t *old_tail; |
| 666 | old_tail = msg->tail; |
| 667 | |
| 668 | msgb_v_put(msg, pdu->msg_type); |
| 669 | |
| 670 | switch (pdu->msg_type) { |
| 671 | case BSSMAP_LE_MSGT_RESET: |
| 672 | rc = osmo_bssmap_le_enc_reset(msg, pdu->reset); |
| 673 | break; |
| 674 | case BSSMAP_LE_MSGT_RESET_ACK: |
| 675 | /* Consists only of the message type. */ |
| 676 | rc = 0; |
| 677 | break; |
| 678 | case BSSMAP_LE_MSGT_PERFORM_LOC_REQ: |
| 679 | rc = osmo_bssmap_le_enc_perform_loc_req(msg, &pdu->perform_loc_req); |
| 680 | break; |
| 681 | case BSSMAP_LE_MSGT_PERFORM_LOC_RESP: |
| 682 | rc = osmo_bssmap_le_enc_perform_loc_resp(msg, &pdu->perform_loc_resp); |
| 683 | break; |
| 684 | case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT: |
| 685 | rc = osmo_bssmap_le_enc_perform_loc_abort(msg, &pdu->perform_loc_abort); |
| 686 | break; |
| 687 | case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO: |
| 688 | rc = osmo_bssmap_le_enc_conn_oriented_info(msg, &pdu->conn_oriented_info); |
| 689 | break; |
| 690 | default: |
| 691 | rc = -ENOTSUP; |
| 692 | } |
| 693 | |
| 694 | if (rc < 0) |
| 695 | return rc; |
| 696 | |
| 697 | return (msg->tail - old_tail); |
| 698 | } |
| 699 | |
| 700 | /*! Decode BSSMAP-LE PDU (3GPP TS 49.031). |
| 701 | * See also osmo_bssap_le_dec(). |
| 702 | * \param[out] pdu Write decoded values here. |
| 703 | * \param[in] data Pointer to BSSMAP-LE PDU raw data. |
| 704 | * \param[in] len Data length to decode. |
| 705 | * \return NULL upon success, a human readable error message on failure. |
| 706 | */ |
| 707 | static int osmo_bssmap_le_dec(struct bssmap_le_pdu *pdu, |
| 708 | struct osmo_bssmap_le_err **err, void *err_ctx, |
| 709 | const uint8_t *data, size_t len) |
| 710 | { |
| 711 | const uint8_t *ies_start; |
| 712 | int ies_len; |
| 713 | struct tlv_parsed tp; |
| 714 | |
Vadim Yanitskiy | 7b9b307 | 2023-02-25 05:52:37 +0700 | [diff] [blame] | 715 | memset(pdu, 0x00, sizeof(*pdu)); |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 716 | |
| 717 | if (len < 1) |
| 718 | DEC_ERR(-EINVAL, -1, -1, LCS_CAUSE_UNSPECIFIED, "zero length"); |
| 719 | pdu->msg_type = data[0]; |
| 720 | |
| 721 | /* BSSMAP-LE IEs */ |
| 722 | ies_start = &data[1]; |
| 723 | ies_len = len - 1; |
| 724 | |
| 725 | if (tlv_parse(&tp, &osmo_bssmap_le_tlvdef, ies_start, ies_len, 0, 0) < 0) |
| 726 | DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "failed to parse TLV structure"); |
| 727 | |
| 728 | switch (pdu->msg_type) { |
| 729 | case BSSMAP_LE_MSGT_RESET: |
| 730 | return osmo_bssmap_le_dec_reset(&pdu->reset, pdu->msg_type, err, err_ctx, &tp); |
| 731 | case BSSMAP_LE_MSGT_RESET_ACK: |
| 732 | /* Consists only of the message type. */ |
| 733 | return 0; |
| 734 | case BSSMAP_LE_MSGT_PERFORM_LOC_REQ: |
| 735 | return osmo_bssmap_le_dec_perform_loc_req(&pdu->perform_loc_req, pdu->msg_type, err, err_ctx, &tp); |
| 736 | case BSSMAP_LE_MSGT_PERFORM_LOC_RESP: |
| 737 | return osmo_bssmap_le_dec_perform_loc_resp(&pdu->perform_loc_resp, pdu->msg_type, err, err_ctx, &tp); |
| 738 | case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT: |
| 739 | return osmo_bssmap_le_dec_perform_loc_abort(&pdu->perform_loc_abort, pdu->msg_type, err, err_ctx, &tp); |
| 740 | case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO: |
| 741 | return osmo_bssmap_le_dec_conn_oriented_info(&pdu->conn_oriented_info, pdu->msg_type, err, err_ctx, |
| 742 | &tp); |
| 743 | default: |
| 744 | DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "Unsupported BSSMAP-LE message type"); |
| 745 | } |
| 746 | } |
| 747 | |
| 748 | /*! Encode BSSAP-LE PDU returned in new msgb (3GPP TS 49.031). |
| 749 | * By spec, BSSAP-LE contains either BSSMAP-LE or DTAP. |
| 750 | * \param[in] pdu PDU data to encode. |
| 751 | * \return msgb with encoded data and l2h set to the start. |
| 752 | */ |
| 753 | struct msgb *osmo_bssap_le_enc(const struct bssap_le_pdu *pdu) |
| 754 | { |
| 755 | struct msgb *msg; |
| 756 | int rc; |
| 757 | |
| 758 | if (pdu->discr != BSSAP_LE_MSG_DISCR_BSSMAP_LE) |
| 759 | return NULL; |
| 760 | |
| 761 | msg = msgb_alloc_headroom(BSSAP_LE_MSG_SIZE, BSSAP_LE_MSG_HEADROOM, |
| 762 | osmo_bssmap_le_msgt_name(pdu->bssmap_le.msg_type)); |
| 763 | if (!msg) |
| 764 | return NULL; |
| 765 | |
| 766 | rc = osmo_bssmap_le_enc(msg, &pdu->bssmap_le); |
| 767 | if (rc <= 0) { |
| 768 | msgb_free(msg); |
| 769 | return NULL; |
| 770 | } |
| 771 | |
| 772 | /* prepend header with final length */ |
| 773 | msg->l2h = msgb_tv_push(msg, pdu->discr, msgb_length(msg)); |
| 774 | |
| 775 | return msg; |
| 776 | } |
| 777 | |
| 778 | /*! Decode BSSAP-LE PDU (3GPP TS 49.031). |
| 779 | * \param[out] pdu Write decoded values here. |
| 780 | * \param[in] data Pointer to BSSMAP-LE PDU raw data. |
| 781 | * \param[in] len Data length to decode. |
| 782 | * \return NULL upon success, a human readable error message on failure. |
| 783 | */ |
| 784 | int osmo_bssap_le_dec(struct bssap_le_pdu *pdu, struct osmo_bssap_le_err **err, void *err_ctx, struct msgb *msg) |
| 785 | { |
| 786 | struct osmo_bssap_le_header *h; |
| 787 | unsigned int check_len; |
| 788 | struct osmo_bssmap_le_err *bssmap_le_err = NULL; |
| 789 | int rc; |
| 790 | |
| 791 | #define BSSAP_LE_DEC_ERR(RC, fmt, args...) do { \ |
| 792 | if (err && !*err) { \ |
| 793 | *err = talloc_zero(err_ctx, struct osmo_bssap_le_err); \ |
| 794 | **err = (struct osmo_bssap_le_err){ \ |
| 795 | .rc = (RC), \ |
| 796 | .logmsg = talloc_asprintf(*err, "Error decoding BSSAP-LE: " fmt, ##args), \ |
| 797 | }; \ |
| 798 | } \ |
| 799 | return RC; \ |
| 800 | } while(0) |
| 801 | |
Vadim Yanitskiy | 7b9b307 | 2023-02-25 05:52:37 +0700 | [diff] [blame] | 802 | memset(pdu, 0x00, sizeof(*pdu)); |
Neels Hofmeyr | 02de87b | 2020-09-18 18:00:50 +0200 | [diff] [blame] | 803 | |
| 804 | h = msgb_l2(msg); |
| 805 | if (!h) |
| 806 | BSSAP_LE_DEC_ERR(-EINVAL, "missing msgb_l2() pointer"); |
| 807 | if (msgb_l2len(msg) < sizeof(*h)) |
| 808 | BSSAP_LE_DEC_ERR(-EINVAL, "message too short for header"); |
| 809 | check_len = msgb_l2len(msg) - sizeof(*h); |
| 810 | if (h->length < check_len) |
| 811 | BSSAP_LE_DEC_ERR(-EINVAL, "message truncated, header length (%u) longer than message (%u)", |
| 812 | h->length, check_len); |
| 813 | |
| 814 | switch (h->type) { |
| 815 | case BSSAP_LE_MSG_DISCR_BSSMAP_LE: |
| 816 | break; |
| 817 | default: |
| 818 | BSSAP_LE_DEC_ERR(-EINVAL, "unsupported discr %u, only BSSMAP-LE is implemented", h->type); |
| 819 | } |
| 820 | |
| 821 | rc = osmo_bssmap_le_dec(&pdu->bssmap_le, err ? &bssmap_le_err : NULL, err_ctx, |
| 822 | h->data, h->length); |
| 823 | if (rc) |
| 824 | BSSAP_LE_DEC_ERR(rc, "%s", |
| 825 | (bssmap_le_err && bssmap_le_err->logmsg) ? |
| 826 | bssmap_le_err->logmsg : "unknown error in BSSMAP-LE part"); |
| 827 | return 0; |
| 828 | } |
| 829 | |
| 830 | const struct value_string osmo_bssmap_le_msgt_names[] = { |
| 831 | { BSSMAP_LE_MSGT_PERFORM_LOC_REQ, "PERFORM LOCATION REQUEST" }, |
| 832 | { BSSMAP_LE_MSGT_PERFORM_LOC_RESP, "PERFORM LOCATION RESPONSE" }, |
| 833 | { BSSMAP_LE_MSGT_PERFORM_LOC_ABORT, "PERFORM LOCATION ABORT" }, |
| 834 | { BSSMAP_LE_MSGT_PERFORM_LOC_INFO, "PERFORM LOCATION INFO" }, |
| 835 | { BSSMAP_LE_MSGT_ASSIST_INFO_REQ, "ASSISTANCE INFORMATION REQUEST" }, |
| 836 | { BSSMAP_LE_MSGT_ASSIST_INFO_RESP, "ASSISTANCE INFORMATION RESPONSE" }, |
| 837 | { BSSMAP_LE_MSGT_CONN_ORIENTED_INFO, "CONNECTION ORIENTED INFORMATON" }, |
| 838 | { BSSMAP_LE_MSGT_CONN_LESS_INFO, "CONNECTIONLESS INFORMATION" }, |
| 839 | { BSSMAP_LE_MSGT_RESET, "RESET" }, |
| 840 | { BSSMAP_LE_MSGT_RESET_ACK, "RESET ACKNOWLEDGE" }, |
| 841 | {} |
| 842 | }; |
| 843 | |
| 844 | const struct value_string osmo_bssmap_le_iei_names[] = { |
| 845 | { BSSMAP_LE_IEI_LCS_QoS, "LCS_QoS" }, |
| 846 | { BSSMAP_LE_IEI_LCS_PRIORITY, "LCS_PRIORITY" }, |
| 847 | { BSSMAP_LE_IEI_LOCATION_TYPE, "LOCATION_TYPE" }, |
| 848 | { BSSMAP_LE_IEI_GANSS_LOCATION_TYPE, "GANSS_LOCATION_TYPE" }, |
| 849 | { BSSMAP_LE_IEI_GEO_LOCATION, "GEO_LOCATION" }, |
| 850 | { BSSMAP_LE_IEI_POSITIONING_DATA, "POSITIONING_DATA" }, |
| 851 | { BSSMAP_LE_IEI_GANSS_POS_DATA, "GANSS_POS_DATA" }, |
| 852 | { BSSMAP_LE_IEI_VELOCITY_DATA, "VELOCITY_DATA" }, |
| 853 | { BSSMAP_LE_IEI_LCS_CAUSE, "LCS_CAUSE" }, |
| 854 | { BSSMAP_LE_IEI_LCS_CLIENT_TYPE, "LCS_CLIENT_TYPE" }, |
| 855 | { BSSMAP_LE_IEI_APDU, "APDU" }, |
| 856 | { BSSMAP_LE_IEI_NET_ELEM_ID, "NET_ELEM_ID" }, |
| 857 | { BSSMAP_LE_IEI_REQ_GPS_ASS_D, "REQ_GPS_ASS_D" }, |
| 858 | { BSSMAP_LE_IEI_REQ_GANSS_ASS_D, "REQ_GANSS_ASS_D" }, |
| 859 | { BSSMAP_LE_IEI_DECIPH_KEYS, "DECIPH_KEYS" }, |
| 860 | { BSSMAP_LE_IEI_RET_ERR_REQ, "RET_ERR_REQ" }, |
| 861 | { BSSMAP_LE_IEI_RET_ERR_CAUSE, "RET_ERR_CAUSE" }, |
| 862 | { BSSMAP_LE_IEI_SEGMENTATION, "SEGMENTATION" }, |
| 863 | { BSSMAP_LE_IEI_CLASSMARK3_INFO, "CLASSMARK3_INFO" }, |
| 864 | { BSSMAP_LE_IEI_CAUSE, "CAUSE" }, |
| 865 | { BSSMAP_LE_IEI_CELL_ID, "CELL_ID" }, |
| 866 | { BSSMAP_LE_IEI_CHOSEN_CHAN, "CHOSEN_CHAN" }, |
| 867 | { BSSMAP_LE_IEI_IMSI, "IMSI" }, |
| 868 | { BSSMAP_LE_IEI_LCS_CAPABILITY, "LCS_CAPABILITY" }, |
| 869 | { BSSMAP_LE_IEI_PKT_MEAS_REP, "PKT_MEAS_REP" }, |
| 870 | { BSSMAP_LE_IEI_CELL_ID_LIST, "CELL_ID_LIST" }, |
| 871 | { BSSMAP_LE_IEI_IMEI, "IMEI" }, |
| 872 | { BSSMAP_LE_IEI_BSS_MLAT_CAP, "BSS_MLAT_CAP" }, |
| 873 | { BSSMAP_LE_IEI_CELL_INFO_LIST, "CELL_INFO_LIST" }, |
| 874 | { BSSMAP_LE_IEI_BTS_RX_ACC_LVL, "BTS_RX_ACC_LVL" }, |
| 875 | { BSSMAP_LE_IEI_MLAT_METHOD, "MLAT_METHOD" }, |
| 876 | { BSSMAP_LE_IEI_MLAT_TA, "MLAT_TA" }, |
| 877 | { BSSMAP_LE_IEI_MS_SYNC_ACC, "MS_SYNC_ACC" }, |
| 878 | { BSSMAP_LE_IEI_SHORT_ID_SET, "SHORT_ID_SET" }, |
| 879 | { BSSMAP_LE_IEI_RANDOM_ID_SET, "RANDOM_ID_SET" }, |
| 880 | { BSSMAP_LE_IEI_SHORT_BSS_ID, "SHORT_BSS_ID" }, |
| 881 | { BSSMAP_LE_IEI_RANDOM_ID, "RANDOM_ID" }, |
| 882 | { BSSMAP_LE_IEI_SHORT_ID, "SHORT_ID" }, |
| 883 | { BSSMAP_LE_IEI_COVERAGE_CLASS, "COVERAGE_CLASS" }, |
| 884 | { BSSMAP_LE_IEI_MTA_ACC_SEC_RQD, "MTA_ACC_SEC_RQD" }, |
| 885 | {} |
| 886 | }; |
| 887 | |
| 888 | /*! Return a human readable string describing a BSSAP-LE PDU. |
| 889 | * \param[out] buf String buffer to write to. |
| 890 | * \param[in] buflen sizeof(buf). |
| 891 | * \param[in] bssap_le Decoded BSSAP-LE PDU data. |
| 892 | * \returns number of chars that would be written, like snprintf(). |
| 893 | */ |
| 894 | int osmo_bssap_le_pdu_to_str_buf(char *buf, size_t buflen, const struct bssap_le_pdu *bssap_le) |
| 895 | { |
| 896 | struct osmo_strbuf sb = { .buf = buf, .len = buflen }; |
| 897 | const struct bssmap_le_pdu *bssmap_le; |
| 898 | |
| 899 | switch (bssap_le->discr) { |
| 900 | case BSSAP_LE_MSG_DISCR_BSSMAP_LE: |
| 901 | bssmap_le = &bssap_le->bssmap_le; |
| 902 | OSMO_STRBUF_PRINTF(sb, "BSSMAP-LE %s", osmo_bssmap_le_msgt_name(bssmap_le->msg_type)); |
| 903 | switch (bssmap_le->msg_type) { |
| 904 | case BSSMAP_LE_MSGT_PERFORM_LOC_REQ: |
| 905 | if (bssmap_le->perform_loc_req.apdu_present) |
| 906 | OSMO_STRBUF_PRINTF(sb, " with BSSLAP %s", |
| 907 | osmo_bsslap_msgt_name(bssmap_le->perform_loc_req.apdu.msg_type)); |
| 908 | break; |
| 909 | |
| 910 | case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO: |
| 911 | OSMO_STRBUF_PRINTF(sb, " with BSSLAP %s", |
| 912 | osmo_bsslap_msgt_name(bssmap_le->conn_oriented_info.apdu.msg_type)); |
| 913 | break; |
| 914 | |
| 915 | default: |
| 916 | break; |
| 917 | } |
| 918 | break; |
| 919 | default: |
| 920 | OSMO_STRBUF_PRINTF(sb, "BSSAP-LE discr %d not implemented", bssap_le->discr); |
| 921 | break; |
| 922 | } |
| 923 | |
| 924 | return sb.chars_needed; |
| 925 | } |
| 926 | |
| 927 | /*! Return a human readable string describing a BSSAP-LE PDU. |
| 928 | * \param[in] ctx Talloc context to allocate string buffer from. |
| 929 | * \param[in] bssap_le Decoded BSSAP-LE PDU data. |
| 930 | * \returns string. |
| 931 | */ |
| 932 | char *osmo_bssap_le_pdu_to_str_c(void *ctx, const struct bssap_le_pdu *bssap_le) |
| 933 | { |
| 934 | OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_bssap_le_pdu_to_str_buf, bssap_le) |
| 935 | } |
| 936 | |
| 937 | /*! @} */ |