This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
laforge gerrit-no-reply at lists.osmocom.orglaforge has submitted this change. ( https://gerrit.osmocom.org/c/libosmocore/+/20334 ) Change subject: add BSSMAP-LE coding for Location Services ...................................................................... add BSSMAP-LE coding for Location Services BSSMAP-LE: add Lb-interface messages between BSC and SMLC: - Reset - Reset Acknowledge - Perform Location Request, possibly containing BSSLAP TA Layer3 - Perform Location Response - Perform Location Abort - Connection Oriented Information containing any BSSLAP APDU Add encoding and decoding tests. Change-Id: I271e59b794bafc0a7ae0eabbf58918f6d7df431d --- M include/Makefile.am A include/osmocom/gsm/bssmap_le.h M include/osmocom/gsm/protocol/gsm_49_031.h M src/gsm/Makefile.am A src/gsm/bssmap_le.c M src/gsm/libosmogsm.map M tests/Makefile.am A tests/bssmap_le/bssmap_le_test.c A tests/bssmap_le/bssmap_le_test.ok M tests/testsuite.at 10 files changed, 1,320 insertions(+), 1 deletion(-) Approvals: Jenkins Builder: Verified laforge: Looks good to me, approved diff --git a/include/Makefile.am b/include/Makefile.am index 19d4043..90f448a 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -93,6 +93,7 @@ osmocom/coding/gsm0503_coding.h \ osmocom/coding/gsm0503_amr_dtx.h \ osmocom/gsm/bsslap.h \ + osmocom/gsm/bssmap_le.h \ osmocom/gsm/gad.h \ osmocom/gsm/gsm0808.h \ osmocom/gsm/gsm29205.h \ diff --git a/include/osmocom/gsm/bssmap_le.h b/include/osmocom/gsm/bssmap_le.h new file mode 100644 index 0000000..1c750c8 --- /dev/null +++ b/include/osmocom/gsm/bssmap_le.h @@ -0,0 +1,81 @@ +/*! \addtogroup bssmap_le + * @{ + * \file bssmap_le.h + * Message encoding and decoding for 3GPP TS 49.031 BSSMAP-LE. + */ +/* + * (C) 2020 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de> + * All Rights Reserved + * + * Author: Neels Hofmeyr <neels at hofmeyr.de> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#pragma once + +#include <osmocom/gsm/protocol/gsm_49_031.h> + +struct osmo_bsslap_err; +struct osmo_gad_err; + +struct osmo_bssmap_le_err { + int rc; + enum bssmap_le_msgt msg_type; + enum bssmap_le_iei iei; + enum lcs_cause cause; + struct osmo_bsslap_err *bsslap_err; + struct osmo_gad_err *gad_err; + char *logmsg; +}; + +struct osmo_bssap_le_err { + int rc; + struct osmo_bssmap_le_err *bssmap_le_err; + void *dtap_err; + char *logmsg; +}; + +enum bssmap_le_msgt osmo_bssmap_le_msgt(const uint8_t *data, uint8_t len); + +extern const struct value_string osmo_bssmap_le_msgt_names[]; +static inline const char *osmo_bssmap_le_msgt_name(enum bssmap_le_msgt val) +{ return get_value_string(osmo_bssmap_le_msgt_names, val); } + +extern const struct value_string osmo_bssmap_le_iei_names[]; +static inline const char *osmo_bssmap_le_iei_name(enum bssmap_le_iei val) +{ return get_value_string(osmo_bssmap_le_iei_names, val); } + +int osmo_lcs_cause_enc(struct msgb *msg, const struct lcs_cause_ie *lcs_cause); +int osmo_lcs_cause_dec(struct lcs_cause_ie *lcs_cause, + enum bssmap_le_msgt msgt, enum bssmap_le_iei iei, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *data, uint8_t len); + +int osmo_bssap_le_pdu_to_str_buf(char *buf, size_t buflen, const struct bssap_le_pdu *bssap_le); +char *osmo_bssap_le_pdu_to_str_c(void *ctx, const struct bssap_le_pdu *bssap_le); + +struct msgb *osmo_bssap_le_enc(const struct bssap_le_pdu *pdu); +int osmo_bssap_le_dec(struct bssap_le_pdu *pdu, struct osmo_bssap_le_err **err, void *err_ctx, struct msgb *msg); + +uint8_t osmo_bssmap_le_ie_enc_location_type(struct msgb *msg, const struct bssmap_le_location_type *location_type); +int osmo_bssmap_le_ie_dec_location_type(struct bssmap_le_location_type *lt, + enum bssmap_le_msgt msgt, enum bssmap_le_iei iei, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *elem, uint8_t len); + +/*! @} */ diff --git a/include/osmocom/gsm/protocol/gsm_49_031.h b/include/osmocom/gsm/protocol/gsm_49_031.h index b44a07e..c6152e1 100644 --- a/include/osmocom/gsm/protocol/gsm_49_031.h +++ b/include/osmocom/gsm/protocol/gsm_49_031.h @@ -29,6 +29,10 @@ #include <stdint.h> #include <stdbool.h> +#include <osmocom/gsm/protocol/gsm_48_071.h> +#include <osmocom/gsm/protocol/gsm_23_032.h> +#include <osmocom/gsm/gsm0808_utils.h> +#include <osmocom/gsm/gsm48.h> /*! 3GPP TS 49.031 10.13 LCS Cause, also in 3GPP TS 48.008 3.2.2.66, which simply refers to the former. */ enum lcs_cause { @@ -58,4 +62,151 @@ uint8_t diag_val; }; +enum bssap_le_msg_discr { + BSSAP_LE_MSG_DISCR_BSSMAP_LE = 0, +}; + +enum bssmap_le_msgt { + BSSMAP_LE_MSGT_PERFORM_LOC_REQ = 0x2b, + BSSMAP_LE_MSGT_PERFORM_LOC_RESP = 0x2d, + BSSMAP_LE_MSGT_PERFORM_LOC_ABORT = 0x2e, + BSSMAP_LE_MSGT_PERFORM_LOC_INFO = 0x2f, + BSSMAP_LE_MSGT_ASSIST_INFO_REQ = 0x20, + BSSMAP_LE_MSGT_ASSIST_INFO_RESP = 0x21, + BSSMAP_LE_MSGT_CONN_ORIENTED_INFO = 0x2a, + BSSMAP_LE_MSGT_CONN_LESS_INFO = 0x3a, + BSSMAP_LE_MSGT_RESET = 0x30, + BSSMAP_LE_MSGT_RESET_ACK = 0x31, +}; + +enum bssmap_le_iei { + BSSMAP_LE_IEI_LCS_QoS = 0x3e, + BSSMAP_LE_IEI_LCS_PRIORITY = 0x43, + BSSMAP_LE_IEI_LOCATION_TYPE = 0x44, + BSSMAP_LE_IEI_GANSS_LOCATION_TYPE = 0x82, + BSSMAP_LE_IEI_GEO_LOCATION = 0x45, + BSSMAP_LE_IEI_POSITIONING_DATA = 0x46, + BSSMAP_LE_IEI_GANSS_POS_DATA = 0x83, + BSSMAP_LE_IEI_VELOCITY_DATA = 0x55, + BSSMAP_LE_IEI_LCS_CAUSE = 0x47, + BSSMAP_LE_IEI_LCS_CLIENT_TYPE = 0x48, + BSSMAP_LE_IEI_APDU = 0x49, + BSSMAP_LE_IEI_NET_ELEM_ID = 0x4a, + BSSMAP_LE_IEI_REQ_GPS_ASS_D = 0x4b, + BSSMAP_LE_IEI_REQ_GANSS_ASS_D = 0x41, + BSSMAP_LE_IEI_DECIPH_KEYS = 0x4c, + BSSMAP_LE_IEI_RET_ERR_REQ = 0x4d, + BSSMAP_LE_IEI_RET_ERR_CAUSE = 0x4e, + BSSMAP_LE_IEI_SEGMENTATION = 0x4f, + BSSMAP_LE_IEI_CLASSMARK3_INFO = 0x13, + BSSMAP_LE_IEI_CAUSE = 0x4, + BSSMAP_LE_IEI_CELL_ID = 0x5, + BSSMAP_LE_IEI_CHOSEN_CHAN = 0x21, + BSSMAP_LE_IEI_IMSI = 0x0, + BSSMAP_LE_IEI_LCS_CAPABILITY = 0x50, + BSSMAP_LE_IEI_PKT_MEAS_REP = 0x51, + BSSMAP_LE_IEI_CELL_ID_LIST = 0x52, + BSSMAP_LE_IEI_IMEI = 0x80, + BSSMAP_LE_IEI_BSS_MLAT_CAP = 0x84, + BSSMAP_LE_IEI_CELL_INFO_LIST = 0x85, + BSSMAP_LE_IEI_BTS_RX_ACC_LVL = 0x86, + BSSMAP_LE_IEI_MLAT_METHOD = 0x87, + BSSMAP_LE_IEI_MLAT_TA = 0x88, + BSSMAP_LE_IEI_MS_SYNC_ACC = 0x89, + BSSMAP_LE_IEI_SHORT_ID_SET = 0x8a, + BSSMAP_LE_IEI_RANDOM_ID_SET = 0x8b, + BSSMAP_LE_IEI_SHORT_BSS_ID = 0x8c, + BSSMAP_LE_IEI_RANDOM_ID = 0x8d, + BSSMAP_LE_IEI_SHORT_ID = 0x8e, + BSSMAP_LE_IEI_COVERAGE_CLASS = 0x8f, + BSSMAP_LE_IEI_MTA_ACC_SEC_RQD = 0x90, +}; + +enum bssmap_le_apdu_proto { + BSSMAP_LE_APDU_PROT_RESERVED = 0, + BSSMAP_LE_APDU_PROT_BSSLAP = 1, + BSSMAP_LE_APDU_PROT_LLP = 2, + BSSMAP_LE_APDU_PROT_SMLCPP = 3, +}; + +enum bssmap_le_location_information { + BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC = 0x0, + BSSMAP_LE_LOC_INFO_ASSIST_TARGET_MS = 0x1, + BSSMAP_LE_LOC_INFO_BC_DECIPHER_KEYS = 0x2, +}; + +enum bssmap_le_positioning_method { + BSSMAP_LE_POS_METHOD_OMITTED = 0x0, + BSSMAP_LE_POS_METHOD_MOBILE_ASSISTED_E_OTD = 0x1, + BSSMAP_LE_POS_METHOD_MOBILE_BASED_E_OTD = 0x2, + BSSMAP_LE_POS_METHOD_ASSISTED_GPS = 0x3, +}; + +struct bssmap_le_location_type { + enum bssmap_le_location_information location_information; + enum bssmap_le_positioning_method positioning_method; +}; + +enum bssmap_le_lcs_client_type { + BSSMAP_LE_LCS_CTYPE_VALUE_ADDED_UNSPECIFIED = 0x0, + BSSMAP_LE_LCS_CTYPE_PLMN_OPER_UNSPECIFIED = 0x20, + BSSMAP_LE_LCS_CTYPE_PLMN_OPER_BCAST_SERVICE = 0x21, + BSSMAP_LE_LCS_CTYPE_PLMN_OPER_OAM = 0x22, + BSSMAP_LE_LCS_CTYPE_PLMN_OPER_ANON_STATS = 0x23, + BSSMAP_LE_LCS_CTYPE_PLMN_OPER_TGT_MS_SVC = 0x24, + BSSMAP_LE_LCS_CTYPE_EMERG_SVC_UNSPECIFIED = 0x30, + BSSMAP_LE_LCS_CTYPE_LI_UNSPECIFIED = 0x40, +}; + +struct bssmap_le_perform_loc_req { + struct bssmap_le_location_type location_type; + struct gsm0808_cell_id cell_id; + + bool lcs_client_type_present; + enum bssmap_le_lcs_client_type lcs_client_type; + + struct osmo_mobile_identity imsi; + struct osmo_mobile_identity imei; + + bool apdu_present; + struct bsslap_pdu apdu; + + bool more_items; /*!< always set this to false */ +}; + +struct bssmap_le_perform_loc_resp { + bool location_estimate_present; + union gad_raw location_estimate; + + struct lcs_cause_ie lcs_cause; + + bool more_items; /*!< always set this to false */ +}; + +struct bssmap_le_conn_oriented_info { + struct bsslap_pdu apdu; + + bool more_items; /*!< always set this to false */ +}; + +struct bssmap_le_pdu { + enum bssmap_le_msgt msg_type; + union { + enum gsm0808_cause reset; + /* reset_ack consists only of the message type */ + struct bssmap_le_perform_loc_req perform_loc_req; + struct bssmap_le_perform_loc_resp perform_loc_resp; + struct lcs_cause_ie perform_loc_abort; + struct bssmap_le_conn_oriented_info conn_oriented_info; + }; +}; + +struct bssap_le_pdu { + enum bssap_le_msg_discr discr; + union { + struct bssmap_le_pdu bssmap_le; + /* future: add DTAP PDU, currently not implemented */ + }; +}; + /*! @} */ diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am index 465bae1..ccb2456 100644 --- a/src/gsm/Makefile.am +++ b/src/gsm/Makefile.am @@ -33,7 +33,7 @@ gsup.c gsup_sms.c gprs_gea.c gsm0503_conv.c oap.c gsm0808_utils.c \ gsm23003.c gsm23236.c mncc.c bts_features.c oap_client.c \ gsm29118.c gsm48_rest_octets.c cbsp.c gsm48049.c i460_mux.c \ - gad.c bsslap.c + gad.c bsslap.c bssmap_le.c libgsmint_la_LDFLAGS = -no-undefined libgsmint_la_LIBADD = $(top_builddir)/src/libosmocore.la diff --git a/src/gsm/bssmap_le.c b/src/gsm/bssmap_le.c new file mode 100644 index 0000000..7954653 --- /dev/null +++ b/src/gsm/bssmap_le.c @@ -0,0 +1,876 @@ +/* 3GPP TS 49.031 BSSMAP-LE protocol definitions */ +/* + * (C) 2020 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de> + * All Rights Reserved + * + * Author: Neels Hofmeyr <neels at hofmeyr.de> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <string.h> + +#include <osmocom/core/byteswap.h> +#include <osmocom/core/endian.h> +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/bssmap_le.h> +#include <osmocom/gsm/bsslap.h> +#include <osmocom/gsm/gad.h> +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/gsm0808.h> + +/*! \addtogroup bssmap_le + * @{ + * \file bssmap_le.c + * Message encoding and decoding for 3GPP TS 49.031 BSSMAP-LE. + */ + +#define BSSAP_LE_MSG_SIZE BSSMAP_MSG_SIZE +#define BSSAP_LE_MSG_HEADROOM BSSMAP_MSG_HEADROOM + +static const struct tlv_definition osmo_bssmap_le_tlvdef = { + .def = { + [BSSMAP_LE_IEI_LCS_QoS] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_LCS_PRIORITY] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_LOCATION_TYPE] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_GANSS_LOCATION_TYPE] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_GEO_LOCATION] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_POSITIONING_DATA] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_GANSS_POS_DATA] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_VELOCITY_DATA] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_LCS_CAUSE] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_LCS_CLIENT_TYPE] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_APDU] = { TLV_TYPE_TL16V }, + [BSSMAP_LE_IEI_NET_ELEM_ID] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_REQ_GPS_ASS_D] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_REQ_GANSS_ASS_D] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_DECIPH_KEYS] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_RET_ERR_REQ] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_RET_ERR_CAUSE] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_SEGMENTATION] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_CLASSMARK3_INFO] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_CAUSE] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_CELL_ID] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_CHOSEN_CHAN] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_IMSI] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_LCS_CAPABILITY] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_PKT_MEAS_REP] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_CELL_ID_LIST] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_IMEI] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_BSS_MLAT_CAP] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_CELL_INFO_LIST] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_BTS_RX_ACC_LVL] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_MLAT_METHOD] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_MLAT_TA] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_MS_SYNC_ACC] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_SHORT_ID_SET] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_RANDOM_ID_SET] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_SHORT_BSS_ID] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_RANDOM_ID] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_SHORT_ID] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_COVERAGE_CLASS] = { TLV_TYPE_TLV }, + [BSSMAP_LE_IEI_MTA_ACC_SEC_RQD] = { TLV_TYPE_TLV }, + }, +}; + +#define DEC_ERR_NO_RETURN(RC, MSG_TYPE, IEI, CAUSE, fmt, args...) do { \ + if (err && !*err) { \ + *err = talloc_zero(err_ctx, struct osmo_bssmap_le_err); \ + **err = (struct osmo_bssmap_le_err){ \ + .rc = (RC), \ + .msg_type = (MSG_TYPE), \ + .iei = (IEI), \ + .cause = (CAUSE), \ + }; \ + (*err)->logmsg = talloc_asprintf(*err, "Error decoding BSSMAP-LE%s%s%s%s%s: " fmt, \ + (MSG_TYPE) >= 0 ? " " : "", \ + (MSG_TYPE) >= 0 ? osmo_bssmap_le_msgt_name(MSG_TYPE) : "", \ + (IEI) >= 0 ? ": " : "", \ + (IEI) >= 0 ? osmo_bssmap_le_iei_name(IEI) : "", \ + (IEI) >= 0 ? " IE" : "", \ + ##args); \ + } \ + } while(0) + +#define DEC_ERR(RC, MSG_TYPE, IEI, CAUSE, fmt, args...) do { \ + DEC_ERR_NO_RETURN(RC, MSG_TYPE, IEI, CAUSE, fmt, ##args); \ + return RC; \ + } while(0) + +#define DEC_IE_MANDATORY(MSG_TYPE, IEI, DEC_FUN, DEC_FUN_ARG) do { \ + const struct tlv_p_entry *e; \ + int rc; \ + if (!(e = TLVP_GET(tp, IEI))) \ + DEC_ERR(-EINVAL, MSG_TYPE, IEI, LCS_CAUSE_DATA_MISSING_IN_REQ, "missing mandatory IE"); \ + rc = DEC_FUN(DEC_FUN_ARG, MSG_TYPE, IEI, err, err_ctx, e->val, e->len); \ + if (rc) \ + DEC_ERR(rc, MSG_TYPE, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \ + } while (0) + +#define DEC_IE_OPTIONAL_FLAG(MSG_TYPE, IEI, DEC_FUN, DEC_FUN_ARG, PRESENCE_FLAG) do { \ + const struct tlv_p_entry *e; \ + int rc; \ + if ((e = TLVP_GET(tp, IEI))) {\ + rc = DEC_FUN(DEC_FUN_ARG, MSG_TYPE, IEI, err, err_ctx, e->val, e->len); \ + if (rc) \ + DEC_ERR(rc, MSG_TYPE, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \ + PRESENCE_FLAG = true; \ + } \ + } while (0) + +#define DEC_IE_OPTIONAL(MSG_TYPE, IEI, DEC_FUN, DEC_FUN_ARG) do { \ + const struct tlv_p_entry *e; \ + int rc; \ + if ((e = TLVP_GET(tp, IEI))) {\ + rc = DEC_FUN(DEC_FUN_ARG, MSG_TYPE, IEI, err, err_ctx, e->val, e->len); \ + if (rc) \ + DEC_ERR(rc, MSG_TYPE, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \ + } \ + } while (0) + +/*! Encode full BSSMAP-LE Location Type IE, including IEI tag and length. + * \param[inout] msg Message buffer to append to. + * \param[in] location_type Values to enconde. + * \returns length of bytes written to the msgb. + */ +uint8_t osmo_bssmap_le_ie_enc_location_type(struct msgb *msg, + const struct bssmap_le_location_type *location_type) +{ + uint8_t *old_tail; + uint8_t *tlv_len; + OSMO_ASSERT(msg); + msgb_put_u8(msg, BSSMAP_LE_IEI_LOCATION_TYPE); + tlv_len = msgb_put(msg, 1); + old_tail = msg->tail; + msgb_put_u8(msg, location_type->location_information); + + switch (location_type->location_information) { + case BSSMAP_LE_LOC_INFO_ASSIST_TARGET_MS: + case BSSMAP_LE_LOC_INFO_BC_DECIPHER_KEYS: + msgb_put_u8(msg, location_type->positioning_method); + break; + default: + break; + } + + *tlv_len = (uint8_t) (msg->tail - old_tail); + return *tlv_len + 2; +} + +/*! Decode BSSMAP-LE Location Type IE value part. + * \param[out] lt Buffer to write decoded values to. + * \param[in] elem Pointer to the value part, the V of a TLV. + * \param[in] len Length, the L of a TLV. + * \returns 0 on success, negative on error; lt is always overwritten: cleared on error, populated with values on + * success. + */ +int osmo_bssmap_le_ie_dec_location_type(struct bssmap_le_location_type *lt, + enum bssmap_le_msgt msgt, enum bssmap_le_iei iei, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *elem, uint8_t len) +{ + *lt = (struct bssmap_le_location_type){}; + + if (!elem || len < 1) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length"); + + lt->location_information = elem[0]; + switch (lt->location_information) { + + case BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC: + if (len != 1) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, + "location info type 'Current Geographic': length should be 1 byte, got %u", len); + lt->positioning_method = BSSMAP_LE_POS_METHOD_OMITTED; + return 0; + + case BSSMAP_LE_LOC_INFO_ASSIST_TARGET_MS: + case BSSMAP_LE_LOC_INFO_BC_DECIPHER_KEYS: + if (len != 2) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, + "location info type %d: length should be 2 bytes, got %u", + lt->location_information, len); + lt->positioning_method = elem[1]; + switch (lt->positioning_method) { + case BSSMAP_LE_POS_METHOD_MOBILE_ASSISTED_E_OTD: + case BSSMAP_LE_POS_METHOD_MOBILE_BASED_E_OTD: + case BSSMAP_LE_POS_METHOD_ASSISTED_GPS: + return 0; + default: + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, + "location info type %d: unknown Positioning Method: %d", + lt->location_information, lt->positioning_method); + } + + default: + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unknown location info type %d", + lt->location_information); + } +} + +/*! Encode full BSSMAP-LE LCS Client Type IE, including IEI tag and length. + * \param[inout] msg Message buffer to append to. + * \param[in] client_type Value to enconde. + * \returns length of bytes written to the msgb. + */ +static uint8_t osmo_bssmap_le_ie_enc_lcs_client_type(struct msgb *msg, enum bssmap_le_lcs_client_type client_type) +{ + OSMO_ASSERT(msg); + msgb_put_u8(msg, BSSMAP_LE_IEI_LCS_CLIENT_TYPE); + /* length */ + msgb_put_u8(msg, 1); + msgb_put_u8(msg, client_type); + return 3; +} + +static int osmo_bssmap_le_ie_dec_lcs_client_type(enum bssmap_le_lcs_client_type *client_type, + enum bssmap_le_msgt msgt, enum bssmap_le_iei iei, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *elem, uint8_t len) +{ + *client_type = 0; + + if (!elem || len < 1) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length"); + + *client_type = elem[0]; + + switch (*client_type) { + case BSSMAP_LE_LCS_CTYPE_VALUE_ADDED_UNSPECIFIED: + case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_UNSPECIFIED: + case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_BCAST_SERVICE: + case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_OAM: + case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_ANON_STATS: + case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_TGT_MS_SVC: + case BSSMAP_LE_LCS_CTYPE_EMERG_SVC_UNSPECIFIED: + case BSSMAP_LE_LCS_CTYPE_LI_UNSPECIFIED: + return 0; + default: + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unknown LCS Client Type: %d", *client_type); + } +} + +/*! Encode the value part of 3GPP TS 49.031 10.13 LCS Cause, without IEI and len. + * Identically used in 3GPP TS 48.008 3.2.2.66. Usage example: + * + * uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_LCS_CAUSE); + * int rc = osmo_lcs_cause_enc(msg, &lcs_cause); + * if (rc < 0) + * goto error; + * *l = rc; + * + * \param[inout] msg Message buffer to append the LCS Cause values to. + * \param[in] lcs_cause LCS Cause values to enconde. + * \returns length of bytes written to the msgb. + */ +int osmo_lcs_cause_enc(struct msgb *msg, const struct lcs_cause_ie *lcs_cause) +{ + msgb_put_u8(msg, lcs_cause->cause_val); + if (lcs_cause->cause_val == LCS_CAUSE_POS_METH_FAILURE && lcs_cause->diag_val_present) { + msgb_put_u8(msg, lcs_cause->diag_val); + return 2; + } + return 1; +} + +/*! Decode the value part of 3GPP TS 49.031 10.13 LCS Cause, without IEI and len. + * Identically used in 3GPP TS 48.008 3.2.2.66. + * + * \param[out] lcs_cause Write decoded LCS Cause values here. + * \param[in] data Encoded cause bytes. + * \param[in] len Length of data in bytes. + * \returns 0 on success, negative on error. + */ +int osmo_lcs_cause_dec(struct lcs_cause_ie *lcs_cause, + enum bssmap_le_msgt msgt, enum bssmap_le_iei iei, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *data, uint8_t len) +{ + *lcs_cause = (struct lcs_cause_ie){}; + + if (!data || len < 1) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length"); + + lcs_cause->present = true; + lcs_cause->cause_val = data[0]; + if (len > 1) { + lcs_cause->diag_val_present = true; + lcs_cause->diag_val = data[1]; + } + if (len > 2) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "expected length <= 2, got %u", len); + + return 0; +} + +static int osmo_bssmap_le_ie_enc_apdu(struct msgb *msg, const struct bsslap_pdu *bsslap) +{ + uint8_t *old_tail; + void *l; + msgb_put_u8(msg, BSSMAP_LE_IEI_APDU); + l = msgb_put(msg, 2); + old_tail = msg->tail; + msgb_put_u8(msg, BSSMAP_LE_APDU_PROT_BSSLAP); + int rc = osmo_bsslap_enc(msg, bsslap); + if (rc <= 0) + return -EINVAL; + osmo_store16be(msg->tail - old_tail, l); + return 0; +} + +static int osmo_bssmap_le_ie_dec_apdu(struct bsslap_pdu *bsslap, + enum bssmap_le_msgt msgt, enum bssmap_le_iei iei, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *data, size_t len) +{ + enum bssmap_le_apdu_proto proto; + struct osmo_bsslap_err *bsslap_err; + + if (!data || len < 1) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length"); + + proto = data[0]; + + switch (proto) { + case BSSMAP_LE_APDU_PROT_BSSLAP: + if (osmo_bsslap_dec(bsslap, &bsslap_err, err_ctx, data + 1, len - 1)) { + DEC_ERR_NO_RETURN(bsslap_err ? bsslap_err->rc : -EINVAL, + msgt, iei, LCS_CAUSE_UNSPECIFIED, + "Error decoding BSSLAP%s%s", + bsslap_err && bsslap_err->logmsg ? ": " : "", + bsslap_err && bsslap_err->logmsg ? bsslap_err->logmsg : ""); + (*err)->bsslap_err = bsslap_err; + return (*err)->rc; + } + return 0; + case BSSMAP_LE_APDU_PROT_LLP: + case BSSMAP_LE_APDU_PROT_SMLCPP: + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Unimplemented APDU type: %d", proto); + default: + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Invalid APDU type: %d", proto); + } +} + +static int osmo_bssmap_le_ie_dec_cell_id(struct gsm0808_cell_id *cell_id, + enum bssmap_le_msgt msgt, enum bssmap_le_iei iei, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *elem, uint8_t len) +{ + int rc; + rc = gsm0808_dec_cell_id(cell_id, elem, len); + if (rc <= 0) + DEC_ERR(rc, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Error decoding Cell Identifier %s", + osmo_hexdump_c(err_ctx, elem, len)); + return 0; +} + +static int osmo_bssmap_le_ie_dec_imsi(struct osmo_mobile_identity *imsi, + enum bssmap_le_msgt msgt, enum bssmap_le_iei iei, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *elem, uint8_t len) +{ + int rc; + rc = osmo_mobile_identity_decode(imsi, elem, len, false); + if (rc || imsi->type != GSM_MI_TYPE_IMSI) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, + "cannot parse IMSI identity %s", osmo_hexdump_c(err_ctx, elem, len)); + return 0; +} + +static int osmo_bssmap_le_ie_dec_imei(struct osmo_mobile_identity *imei, + enum bssmap_le_msgt msgt, enum bssmap_le_iei iei, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *elem, uint8_t len) +{ + int rc; + rc = osmo_mobile_identity_decode(imei, elem, len, false); + if (rc || imei->type != GSM_MI_TYPE_IMEI) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, + "cannot parse IMEI identity %s", osmo_hexdump_c(err_ctx, elem, len)); + return 0; +} + +static int osmo_bssmap_le_ie_dec_gad(union gad_raw *gad, + enum bssmap_le_msgt msgt, enum bssmap_le_iei iei, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *elem, uint8_t len) +{ + struct osmo_gad_err *gad_err; + if (osmo_gad_raw_read(gad, &gad_err, err_ctx, elem, len)) { + DEC_ERR_NO_RETURN(gad_err ? gad_err->rc : -EINVAL, + msgt, BSSMAP_LE_IEI_GEO_LOCATION, LCS_CAUSE_UNSPECIFIED, + "Error decoding GAD%s%s", + gad_err && gad_err->logmsg ? ": " : "", + gad_err && gad_err->logmsg ? gad_err->logmsg : ""); + (*err)->gad_err = gad_err; + return (*err)->rc; + } + return 0; +} + +struct osmo_bssap_le_header { + uint8_t type; + uint8_t length; + uint8_t data[0]; +} __attribute__((packed)); + +/*! Return the BSSMAP-LE msg_type from a BSSAP-LE PDU, e.g. from a msgb_l3(). + * \param[in] data BSSAP-LE PDU data, starting with BSSAP-LE discriminator. + * \param[in] len Length of data in bytes. + * \returns bssmap_le_msgt or negative on error or non-BSSMAP-LE discriminator. */ +enum bssmap_le_msgt osmo_bssmap_le_msgt(const uint8_t *data, uint8_t len) +{ + const struct osmo_bssap_le_header *h = (void*)data; + if (!data || len < sizeof(struct osmo_bssap_le_header) + 1) + return -1; + if (h->type != BSSAP_LE_MSG_DISCR_BSSMAP_LE) + return -1; + return h->data[0]; +} + +static int osmo_bssmap_le_enc_reset(struct msgb *msg, enum gsm0808_cause cause) +{ + /* The BSSMAP-LE Reset Cause is defined as identical to the 3GPP TS 48.008 Cause. */ + gsm0808_enc_cause(msg, cause); + return 0; +} + +static int osmo_bssmap_le_dec_reset(enum gsm0808_cause *cause, + enum bssmap_le_msgt msgt, + struct osmo_bssmap_le_err **err, void *err_ctx, + const struct tlv_parsed *tp) +{ + const struct tlv_p_entry *e; + + if (!(e = TLVP_GET(tp, BSSMAP_LE_IEI_CAUSE))) + DEC_ERR(-EINVAL, msgt, BSSMAP_LE_IEI_CAUSE, LCS_CAUSE_DATA_MISSING_IN_REQ, "missing mandatory IE"); + + *cause = gsm0808_get_cause(tp); + if (*cause < 0) + DEC_ERR(-EINVAL, msgt, BSSMAP_LE_IEI_CAUSE, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); + + return 0; +} + +static int osmo_bssmap_le_enc_perform_loc_req(struct msgb *msg, const struct bssmap_le_perform_loc_req *params) +{ + osmo_bssmap_le_ie_enc_location_type(msg, ¶ms->location_type); + + gsm0808_enc_cell_id(msg, ¶ms->cell_id); + + if (params->lcs_client_type_present) + osmo_bssmap_le_ie_enc_lcs_client_type(msg, params->lcs_client_type); + + if (params->apdu_present) { + int rc = osmo_bssmap_le_ie_enc_apdu(msg, ¶ms->apdu); + if (rc < 0) + return rc; + } + + if (params->imsi.type == GSM_MI_TYPE_IMSI) { + uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_IMSI); + int rc = osmo_mobile_identity_encode_msgb(msg, ¶ms->imsi, false); + if (rc < 0) + return rc; + *l = rc; + } + + if (params->imei.type == GSM_MI_TYPE_IMEI) { + uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_IMEI); + int rc = osmo_mobile_identity_encode_msgb(msg, ¶ms->imei, false); + if (rc < 0) + return rc; + *l = rc; + } + return 0; +} + +static int osmo_bssmap_le_dec_perform_loc_req(struct bssmap_le_perform_loc_req *params, + enum bssmap_le_msgt msgt, + struct osmo_bssmap_le_err **err, void *err_ctx, + const struct tlv_parsed *tp) +{ + *params = (struct bssmap_le_perform_loc_req){}; + + DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_LOCATION_TYPE, osmo_bssmap_le_ie_dec_location_type, + ¶ms->location_type); + DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_CELL_ID, osmo_bssmap_le_ie_dec_cell_id, + ¶ms->cell_id); + DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_CLIENT_TYPE, osmo_bssmap_le_ie_dec_lcs_client_type, + ¶ms->lcs_client_type, params->lcs_client_type_present); + DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_APDU, osmo_bssmap_le_ie_dec_apdu, ¶ms->apdu, + params->apdu_present); + DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_IMSI, osmo_bssmap_le_ie_dec_imsi, ¶ms->imsi); + DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_IMEI, osmo_bssmap_le_ie_dec_imei, ¶ms->imei); + + return 0; +} + +static int osmo_bssmap_le_enc_perform_loc_resp(struct msgb *msg, const struct bssmap_le_perform_loc_resp *params) +{ + if (params->location_estimate_present) { + uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_GEO_LOCATION); + int rc = osmo_gad_raw_write(msg, ¶ms->location_estimate); + if (rc < 0) + return rc; + *l = rc; + } + + if (params->lcs_cause.present) { + uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_LCS_CAUSE); + int rc = osmo_lcs_cause_enc(msg, ¶ms->lcs_cause); + if (rc < 0) + return rc; + *l = rc; + } + return 0; +} + +static int osmo_bssmap_le_dec_perform_loc_resp(struct bssmap_le_perform_loc_resp *params, + enum bssmap_le_msgt msgt, + struct osmo_bssmap_le_err **err, void *err_ctx, + const struct tlv_parsed *tp) +{ + *params = (struct bssmap_le_perform_loc_resp){}; + + DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_GEO_LOCATION, osmo_bssmap_le_ie_dec_gad, ¶ms->location_estimate, + params->location_estimate_present); + DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_LCS_CAUSE, osmo_lcs_cause_dec, ¶ms->lcs_cause); + + return 0; +} + +static int osmo_bssmap_le_enc_perform_loc_abort(struct msgb *msg, const struct lcs_cause_ie *params) +{ + uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_LCS_CAUSE); + int rc = osmo_lcs_cause_enc(msg, params); + if (rc < 0) + return rc; + *l = rc; + return 0; +} + +static int osmo_bssmap_le_dec_perform_loc_abort(struct lcs_cause_ie *params, + enum bssmap_le_msgt msgt, + struct osmo_bssmap_le_err **err, void *err_ctx, + const struct tlv_parsed *tp) +{ + *params = (struct lcs_cause_ie){}; + + DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_LCS_CAUSE, osmo_lcs_cause_dec, params); + return 0; +} + +static int osmo_bssmap_le_enc_conn_oriented_info(struct msgb *msg, + const struct bssmap_le_conn_oriented_info *params) +{ + return osmo_bssmap_le_ie_enc_apdu(msg, ¶ms->apdu); +} + +static int osmo_bssmap_le_dec_conn_oriented_info(struct bssmap_le_conn_oriented_info *params, + enum bssmap_le_msgt msgt, + struct osmo_bssmap_le_err **err, void *err_ctx, + const struct tlv_parsed *tp) +{ + *params = (struct bssmap_le_conn_oriented_info){}; + DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_APDU, osmo_bssmap_le_ie_dec_apdu, ¶ms->apdu); + return 0; +} + +/*! Encode BSSMAP-LE PDU and add to msgb (3GPP TS 49.031). + * See also osmo_bssap_le_enc(). + * \param[out] msg msgb to append to. + * \param[in] pdu PDU data to encode. + * \return number of bytes written, negative on error. + */ +static int osmo_bssmap_le_enc(struct msgb *msg, const struct bssmap_le_pdu *pdu) +{ + int rc; + uint8_t *old_tail; + old_tail = msg->tail; + + msgb_v_put(msg, pdu->msg_type); + + switch (pdu->msg_type) { + case BSSMAP_LE_MSGT_RESET: + rc = osmo_bssmap_le_enc_reset(msg, pdu->reset); + break; + case BSSMAP_LE_MSGT_RESET_ACK: + /* Consists only of the message type. */ + rc = 0; + break; + case BSSMAP_LE_MSGT_PERFORM_LOC_REQ: + rc = osmo_bssmap_le_enc_perform_loc_req(msg, &pdu->perform_loc_req); + break; + case BSSMAP_LE_MSGT_PERFORM_LOC_RESP: + rc = osmo_bssmap_le_enc_perform_loc_resp(msg, &pdu->perform_loc_resp); + break; + case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT: + rc = osmo_bssmap_le_enc_perform_loc_abort(msg, &pdu->perform_loc_abort); + break; + case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO: + rc = osmo_bssmap_le_enc_conn_oriented_info(msg, &pdu->conn_oriented_info); + break; + default: + rc = -ENOTSUP; + } + + if (rc < 0) + return rc; + + return (msg->tail - old_tail); +} + +/*! Decode BSSMAP-LE PDU (3GPP TS 49.031). + * See also osmo_bssap_le_dec(). + * \param[out] pdu Write decoded values here. + * \param[in] data Pointer to BSSMAP-LE PDU raw data. + * \param[in] len Data length to decode. + * \return NULL upon success, a human readable error message on failure. + */ +static int osmo_bssmap_le_dec(struct bssmap_le_pdu *pdu, + struct osmo_bssmap_le_err **err, void *err_ctx, + const uint8_t *data, size_t len) +{ + const uint8_t *ies_start; + int ies_len; + struct tlv_parsed tp; + + *pdu = (struct bssmap_le_pdu){}; + + if (len < 1) + DEC_ERR(-EINVAL, -1, -1, LCS_CAUSE_UNSPECIFIED, "zero length"); + pdu->msg_type = data[0]; + + /* BSSMAP-LE IEs */ + ies_start = &data[1]; + ies_len = len - 1; + + if (tlv_parse(&tp, &osmo_bssmap_le_tlvdef, ies_start, ies_len, 0, 0) < 0) + DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "failed to parse TLV structure"); + + switch (pdu->msg_type) { + case BSSMAP_LE_MSGT_RESET: + return osmo_bssmap_le_dec_reset(&pdu->reset, pdu->msg_type, err, err_ctx, &tp); + case BSSMAP_LE_MSGT_RESET_ACK: + /* Consists only of the message type. */ + return 0; + case BSSMAP_LE_MSGT_PERFORM_LOC_REQ: + return osmo_bssmap_le_dec_perform_loc_req(&pdu->perform_loc_req, pdu->msg_type, err, err_ctx, &tp); + case BSSMAP_LE_MSGT_PERFORM_LOC_RESP: + return osmo_bssmap_le_dec_perform_loc_resp(&pdu->perform_loc_resp, pdu->msg_type, err, err_ctx, &tp); + case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT: + return osmo_bssmap_le_dec_perform_loc_abort(&pdu->perform_loc_abort, pdu->msg_type, err, err_ctx, &tp); + case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO: + return osmo_bssmap_le_dec_conn_oriented_info(&pdu->conn_oriented_info, pdu->msg_type, err, err_ctx, + &tp); + default: + DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "Unsupported BSSMAP-LE message type"); + } +} + +/*! Encode BSSAP-LE PDU returned in new msgb (3GPP TS 49.031). + * By spec, BSSAP-LE contains either BSSMAP-LE or DTAP. + * \param[in] pdu PDU data to encode. + * \return msgb with encoded data and l2h set to the start. + */ +struct msgb *osmo_bssap_le_enc(const struct bssap_le_pdu *pdu) +{ + struct msgb *msg; + int rc; + + if (pdu->discr != BSSAP_LE_MSG_DISCR_BSSMAP_LE) + return NULL; + + msg = msgb_alloc_headroom(BSSAP_LE_MSG_SIZE, BSSAP_LE_MSG_HEADROOM, + osmo_bssmap_le_msgt_name(pdu->bssmap_le.msg_type)); + if (!msg) + return NULL; + + rc = osmo_bssmap_le_enc(msg, &pdu->bssmap_le); + if (rc <= 0) { + msgb_free(msg); + return NULL; + } + + /* prepend header with final length */ + msg->l2h = msgb_tv_push(msg, pdu->discr, msgb_length(msg)); + + return msg; +} + +/*! Decode BSSAP-LE PDU (3GPP TS 49.031). + * \param[out] pdu Write decoded values here. + * \param[in] data Pointer to BSSMAP-LE PDU raw data. + * \param[in] len Data length to decode. + * \return NULL upon success, a human readable error message on failure. + */ +int osmo_bssap_le_dec(struct bssap_le_pdu *pdu, struct osmo_bssap_le_err **err, void *err_ctx, struct msgb *msg) +{ + struct osmo_bssap_le_header *h; + unsigned int check_len; + struct osmo_bssmap_le_err *bssmap_le_err = NULL; + int rc; + +#define BSSAP_LE_DEC_ERR(RC, fmt, args...) do { \ + if (err && !*err) { \ + *err = talloc_zero(err_ctx, struct osmo_bssap_le_err); \ + **err = (struct osmo_bssap_le_err){ \ + .rc = (RC), \ + .logmsg = talloc_asprintf(*err, "Error decoding BSSAP-LE: " fmt, ##args), \ + }; \ + } \ + return RC; \ + } while(0) + + *pdu = (struct bssap_le_pdu){}; + + h = msgb_l2(msg); + if (!h) + BSSAP_LE_DEC_ERR(-EINVAL, "missing msgb_l2() pointer"); + if (msgb_l2len(msg) < sizeof(*h)) + BSSAP_LE_DEC_ERR(-EINVAL, "message too short for header"); + check_len = msgb_l2len(msg) - sizeof(*h); + if (h->length < check_len) + BSSAP_LE_DEC_ERR(-EINVAL, "message truncated, header length (%u) longer than message (%u)", + h->length, check_len); + + switch (h->type) { + case BSSAP_LE_MSG_DISCR_BSSMAP_LE: + break; + default: + BSSAP_LE_DEC_ERR(-EINVAL, "unsupported discr %u, only BSSMAP-LE is implemented", h->type); + } + + rc = osmo_bssmap_le_dec(&pdu->bssmap_le, err ? &bssmap_le_err : NULL, err_ctx, + h->data, h->length); + if (rc) + BSSAP_LE_DEC_ERR(rc, "%s", + (bssmap_le_err && bssmap_le_err->logmsg) ? + bssmap_le_err->logmsg : "unknown error in BSSMAP-LE part"); + return 0; +} + +const struct value_string osmo_bssmap_le_msgt_names[] = { + { BSSMAP_LE_MSGT_PERFORM_LOC_REQ, "PERFORM LOCATION REQUEST" }, + { BSSMAP_LE_MSGT_PERFORM_LOC_RESP, "PERFORM LOCATION RESPONSE" }, + { BSSMAP_LE_MSGT_PERFORM_LOC_ABORT, "PERFORM LOCATION ABORT" }, + { BSSMAP_LE_MSGT_PERFORM_LOC_INFO, "PERFORM LOCATION INFO" }, + { BSSMAP_LE_MSGT_ASSIST_INFO_REQ, "ASSISTANCE INFORMATION REQUEST" }, + { BSSMAP_LE_MSGT_ASSIST_INFO_RESP, "ASSISTANCE INFORMATION RESPONSE" }, + { BSSMAP_LE_MSGT_CONN_ORIENTED_INFO, "CONNECTION ORIENTED INFORMATON" }, + { BSSMAP_LE_MSGT_CONN_LESS_INFO, "CONNECTIONLESS INFORMATION" }, + { BSSMAP_LE_MSGT_RESET, "RESET" }, + { BSSMAP_LE_MSGT_RESET_ACK, "RESET ACKNOWLEDGE" }, + {} +}; + +const struct value_string osmo_bssmap_le_iei_names[] = { + { BSSMAP_LE_IEI_LCS_QoS, "LCS_QoS" }, + { BSSMAP_LE_IEI_LCS_PRIORITY, "LCS_PRIORITY" }, + { BSSMAP_LE_IEI_LOCATION_TYPE, "LOCATION_TYPE" }, + { BSSMAP_LE_IEI_GANSS_LOCATION_TYPE, "GANSS_LOCATION_TYPE" }, + { BSSMAP_LE_IEI_GEO_LOCATION, "GEO_LOCATION" }, + { BSSMAP_LE_IEI_POSITIONING_DATA, "POSITIONING_DATA" }, + { BSSMAP_LE_IEI_GANSS_POS_DATA, "GANSS_POS_DATA" }, + { BSSMAP_LE_IEI_VELOCITY_DATA, "VELOCITY_DATA" }, + { BSSMAP_LE_IEI_LCS_CAUSE, "LCS_CAUSE" }, + { BSSMAP_LE_IEI_LCS_CLIENT_TYPE, "LCS_CLIENT_TYPE" }, + { BSSMAP_LE_IEI_APDU, "APDU" }, + { BSSMAP_LE_IEI_NET_ELEM_ID, "NET_ELEM_ID" }, + { BSSMAP_LE_IEI_REQ_GPS_ASS_D, "REQ_GPS_ASS_D" }, + { BSSMAP_LE_IEI_REQ_GANSS_ASS_D, "REQ_GANSS_ASS_D" }, + { BSSMAP_LE_IEI_DECIPH_KEYS, "DECIPH_KEYS" }, + { BSSMAP_LE_IEI_RET_ERR_REQ, "RET_ERR_REQ" }, + { BSSMAP_LE_IEI_RET_ERR_CAUSE, "RET_ERR_CAUSE" }, + { BSSMAP_LE_IEI_SEGMENTATION, "SEGMENTATION" }, + { BSSMAP_LE_IEI_CLASSMARK3_INFO, "CLASSMARK3_INFO" }, + { BSSMAP_LE_IEI_CAUSE, "CAUSE" }, + { BSSMAP_LE_IEI_CELL_ID, "CELL_ID" }, + { BSSMAP_LE_IEI_CHOSEN_CHAN, "CHOSEN_CHAN" }, + { BSSMAP_LE_IEI_IMSI, "IMSI" }, + { BSSMAP_LE_IEI_LCS_CAPABILITY, "LCS_CAPABILITY" }, + { BSSMAP_LE_IEI_PKT_MEAS_REP, "PKT_MEAS_REP" }, + { BSSMAP_LE_IEI_CELL_ID_LIST, "CELL_ID_LIST" }, + { BSSMAP_LE_IEI_IMEI, "IMEI" }, + { BSSMAP_LE_IEI_BSS_MLAT_CAP, "BSS_MLAT_CAP" }, + { BSSMAP_LE_IEI_CELL_INFO_LIST, "CELL_INFO_LIST" }, + { BSSMAP_LE_IEI_BTS_RX_ACC_LVL, "BTS_RX_ACC_LVL" }, + { BSSMAP_LE_IEI_MLAT_METHOD, "MLAT_METHOD" }, + { BSSMAP_LE_IEI_MLAT_TA, "MLAT_TA" }, + { BSSMAP_LE_IEI_MS_SYNC_ACC, "MS_SYNC_ACC" }, + { BSSMAP_LE_IEI_SHORT_ID_SET, "SHORT_ID_SET" }, + { BSSMAP_LE_IEI_RANDOM_ID_SET, "RANDOM_ID_SET" }, + { BSSMAP_LE_IEI_SHORT_BSS_ID, "SHORT_BSS_ID" }, + { BSSMAP_LE_IEI_RANDOM_ID, "RANDOM_ID" }, + { BSSMAP_LE_IEI_SHORT_ID, "SHORT_ID" }, + { BSSMAP_LE_IEI_COVERAGE_CLASS, "COVERAGE_CLASS" }, + { BSSMAP_LE_IEI_MTA_ACC_SEC_RQD, "MTA_ACC_SEC_RQD" }, + {} +}; + +/*! Return a human readable string describing a BSSAP-LE PDU. + * \param[out] buf String buffer to write to. + * \param[in] buflen sizeof(buf). + * \param[in] bssap_le Decoded BSSAP-LE PDU data. + * \returns number of chars that would be written, like snprintf(). + */ +int osmo_bssap_le_pdu_to_str_buf(char *buf, size_t buflen, const struct bssap_le_pdu *bssap_le) +{ + struct osmo_strbuf sb = { .buf = buf, .len = buflen }; + const struct bssmap_le_pdu *bssmap_le; + + switch (bssap_le->discr) { + case BSSAP_LE_MSG_DISCR_BSSMAP_LE: + bssmap_le = &bssap_le->bssmap_le; + OSMO_STRBUF_PRINTF(sb, "BSSMAP-LE %s", osmo_bssmap_le_msgt_name(bssmap_le->msg_type)); + switch (bssmap_le->msg_type) { + case BSSMAP_LE_MSGT_PERFORM_LOC_REQ: + if (bssmap_le->perform_loc_req.apdu_present) + OSMO_STRBUF_PRINTF(sb, " with BSSLAP %s", + osmo_bsslap_msgt_name(bssmap_le->perform_loc_req.apdu.msg_type)); + break; + + case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO: + OSMO_STRBUF_PRINTF(sb, " with BSSLAP %s", + osmo_bsslap_msgt_name(bssmap_le->conn_oriented_info.apdu.msg_type)); + break; + + default: + break; + } + break; + default: + OSMO_STRBUF_PRINTF(sb, "BSSAP-LE discr %d not implemented", bssap_le->discr); + break; + } + + return sb.chars_needed; +} + +/*! Return a human readable string describing a BSSAP-LE PDU. + * \param[in] ctx Talloc context to allocate string buffer from. + * \param[in] bssap_le Decoded BSSAP-LE PDU data. + * \returns string. + */ +char *osmo_bssap_le_pdu_to_str_c(void *ctx, const struct bssap_le_pdu *bssap_le) +{ + OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_bssap_le_pdu_to_str_buf, bssap_le) +} + +/*! @} */ diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index 257c3fa..89d81b3 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -705,6 +705,17 @@ osmo_nri_ranges_to_str_buf; osmo_nri_ranges_to_str_c; +osmo_bssmap_le_msgt_names; +osmo_bssap_le_enc; +osmo_bssap_le_dec; +osmo_lcs_cause_enc; +osmo_lcs_cause_dec; +osmo_bssmap_le_ie_enc_location_type; +osmo_bssmap_le_ie_dec_location_type; +osmo_bssmap_le_msgt; +osmo_bssap_le_pdu_to_str_buf; +osmo_bssap_le_pdu_to_str_c; + osmo_bsslap_enc; osmo_bsslap_dec; osmo_bsslap_msgt_names; diff --git a/tests/Makefile.am b/tests/Makefile.am index fc99485..152eb60 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -40,6 +40,7 @@ bitgen/bitgen_test \ gad/gad_test \ bsslap/bsslap_test \ + bssmap_le/bssmap_le_test \ $(NULL) if ENABLE_MSGFILE @@ -289,6 +290,9 @@ bsslap_bsslap_test_SOURCES = bsslap/bsslap_test.c bsslap_bsslap_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la +bssmap_le_bssmap_le_test_SOURCES = bssmap_le/bssmap_le_test.c +bssmap_le_bssmap_le_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la + # The `:;' works around a Bash 3.2 bug when the output is not writeable. $(srcdir)/package.m4: $(top_srcdir)/configure.ac :;{ \ @@ -371,6 +375,7 @@ bitgen/bitgen_test.ok \ gad/gad_test.ok \ bsslap/bsslap_test.ok \ + bssmap_le/bssmap_le_test.ok \ $(NULL) if ENABLE_LIBSCTP diff --git a/tests/bssmap_le/bssmap_le_test.c b/tests/bssmap_le/bssmap_le_test.c new file mode 100644 index 0000000..59c7ed2 --- /dev/null +++ b/tests/bssmap_le/bssmap_le_test.c @@ -0,0 +1,177 @@ +#include <stdio.h> + +#include <osmocom/core/utils.h> +#include <osmocom/gsm/bssmap_le.h> + +struct bssmap_le_pdu bssmap_le_test_pdus[] = { + { + .msg_type = BSSMAP_LE_MSGT_RESET, + .reset = GSM0808_CAUSE_EQUIPMENT_FAILURE, + }, + { + .msg_type = BSSMAP_LE_MSGT_RESET_ACK, + }, + { + .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_REQ, + .perform_loc_req = { + .location_type = { + .location_information = BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC, + }, + + .cell_id = { + .id_discr = CELL_IDENT_LAC_AND_CI, + .id.lac_and_ci = { + .lac = 23, + .ci = 42, + }, + }, + + .lcs_client_type_present = true, + .lcs_client_type = BSSMAP_LE_LCS_CTYPE_VALUE_ADDED_UNSPECIFIED, + + .imsi = { + .type = GSM_MI_TYPE_IMSI, + .imsi = "1234567890", + }, + + .imei = { + .type = GSM_MI_TYPE_IMEI, + .imei = "123456789012345", + }, + + .apdu_present = true, + .apdu = { + .msg_type = BSSLAP_MSGT_TA_LAYER3, + .ta_layer3 = { + .ta = 23, + }, + }, + }, + }, + { + .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP, + .perform_loc_resp = { + .location_estimate_present = true, + .location_estimate = { + .ell_point_unc_circle = { + .h = { .type = GAD_TYPE_ELL_POINT_UNC_CIRCLE }, + .lat = { 1, 2, 3 }, + .lon = { 4, 5, 6 }, + .unc = 123, + }, + }, + }, + }, + { + .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP, + .perform_loc_resp = { + .lcs_cause = { + .present = true, + .cause_val = LCS_CAUSE_REQUEST_ABORTED, + }, + }, + }, + { + .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP, + .perform_loc_resp = { + .lcs_cause = { + .present = true, + .cause_val = LCS_CAUSE_POS_METH_FAILURE, + .diag_val_present = true, + .diag_val = 23, + }, + }, + }, + { + .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_ABORT, + .perform_loc_abort = { + .present = true, + .cause_val = LCS_CAUSE_REQUEST_ABORTED, + }, + }, + { + .msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO, + .conn_oriented_info = { + .apdu = { + .msg_type = BSSLAP_MSGT_TA_REQUEST, + }, + }, + }, + { + .msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO, + .conn_oriented_info = { + .apdu = { + .msg_type = BSSLAP_MSGT_TA_RESPONSE, + .ta_response = { + .cell_id = 23, + .ta = 42, + }, + }, + }, + }, + { + .msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO, + .conn_oriented_info = { + .apdu = { + .msg_type = BSSLAP_MSGT_REJECT, + .reject = BSSLAP_CAUSE_CONGESTION, + }, + }, + }, +}; + +void test_bssmap_le_enc_dec() +{ + struct bssmap_le_pdu *pdu; + printf("--- %s\n", __func__); + + for (pdu = bssmap_le_test_pdus; (pdu - bssmap_le_test_pdus) < ARRAY_SIZE(bssmap_le_test_pdus); pdu++) { + struct msgb *msg; + struct bssap_le_pdu enc_pdu = { + .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE, + .bssmap_le = *pdu, + }; + struct bssap_le_pdu dec_pdu; + struct osmo_bssap_le_err *err; + void *loop_ctx; + int rc; + + msg = osmo_bssap_le_enc(&enc_pdu); + if (!msg) { + printf("[%ld] %s: ERROR: failed to encode pdu\n", (pdu - bssmap_le_test_pdus), + osmo_bssmap_le_msgt_name(pdu->msg_type)); + goto loop_end; + } + loop_ctx = msg; + + memset(&dec_pdu, 0xff, sizeof(dec_pdu)); + rc = osmo_bssap_le_dec(&dec_pdu, &err, loop_ctx, msg); + if (rc) { + printf("[%ld] %s: ERROR: failed to decode pdu: %s\n", (pdu - bssmap_le_test_pdus), + osmo_bssmap_le_msgt_name(pdu->msg_type), err->logmsg); + printf(" encoded data: %s\n", osmo_hexdump(msg->data, msg->len)); + goto loop_end; + } + + if (memcmp(&enc_pdu, &dec_pdu, sizeof(dec_pdu))) { + printf("[%ld] %s: ERROR: decoded PDU != encoded PDU\n", (pdu - bssmap_le_test_pdus), + osmo_bssmap_le_msgt_name(pdu->msg_type)); + printf(" original struct: %s\n", osmo_hexdump((void*)&enc_pdu, sizeof(enc_pdu))); + printf(" decoded struct: %s\n", osmo_hexdump((void*)&dec_pdu, sizeof(dec_pdu))); + printf(" encoded data: %s\n", osmo_hexdump(msg->data, msg->len)); + goto loop_end; + } + + printf("[%ld] %s: ok (encoded len = %d)\n", (pdu - bssmap_le_test_pdus), + osmo_bssmap_le_msgt_name(pdu->msg_type), msg->len); + +loop_end: + msgb_free(msg); + } +} + +int main() +{ + test_bssmap_le_enc_dec(); + return 0; +} diff --git a/tests/bssmap_le/bssmap_le_test.ok b/tests/bssmap_le/bssmap_le_test.ok new file mode 100644 index 0000000..a6f0dee --- /dev/null +++ b/tests/bssmap_le/bssmap_le_test.ok @@ -0,0 +1,11 @@ +--- test_bssmap_le_enc_dec +[0] RESET: ok (encoded len = 6) +[1] RESET ACKNOWLEDGE: ok (encoded len = 3) +[2] PERFORM LOCATION REQUEST: ok (encoded len = 41) +[3] PERFORM LOCATION RESPONSE: ok (encoded len = 13) +[4] PERFORM LOCATION RESPONSE: ok (encoded len = 6) +[5] PERFORM LOCATION RESPONSE: ok (encoded len = 7) +[6] PERFORM LOCATION ABORT: ok (encoded len = 6) +[7] CONNECTION ORIENTED INFORMATON: ok (encoded len = 8) +[8] CONNECTION ORIENTED INFORMATON: ok (encoded len = 13) +[9] CONNECTION ORIENTED INFORMATON: ok (encoded len = 10) diff --git a/tests/testsuite.at b/tests/testsuite.at index 0923c25..43f515a 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -415,3 +415,9 @@ cat $abs_srcdir/bsslap/bsslap_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bsslap/bsslap_test], [0], [expout], [ignore]) AT_CLEANUP + +AT_SETUP([bssmap_le]) +AT_KEYWORDS([bssmap_le]) +cat $abs_srcdir/bssmap_le/bssmap_le_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/bssmap_le/bssmap_le_test], [0], [expout], [ignore]) +AT_CLEANUP -- To view, visit https://gerrit.osmocom.org/c/libosmocore/+/20334 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: libosmocore Gerrit-Branch: master Gerrit-Change-Id: I271e59b794bafc0a7ae0eabbf58918f6d7df431d Gerrit-Change-Number: 20334 Gerrit-PatchSet: 6 Gerrit-Owner: neels <nhofmeyr at sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge at osmocom.org> Gerrit-Reviewer: neels <nhofmeyr at sysmocom.de> Gerrit-MessageType: merged -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20201008/abaae4d9/attachment.htm>