diff options
author | Neels Hofmeyr <neels@hofmeyr.de> | 2020-09-18 18:00:50 +0200 |
---|---|---|
committer | Neels Hofmeyr <neels@hofmeyr.de> | 2020-10-07 15:19:43 +0200 |
commit | c6848f41455068a3bb59ca937293d2c0da0af14d (patch) | |
tree | 395339bc1d2d68cf7202f45ac9c69f961fdba94c /src/gsm | |
parent | 086bd33f18fa79eb239a06e404915f3ba3c20e3f (diff) |
add BSSLAP coding for Location Services
BSSLAP: there are APDUs transferred in BSSMAP-LE Connection Oriented
Information messages on Lb between BSC and SMLC.
Add BSSLAP coding for these APDU messages:
- TA Layer3
- TA Request
- TA Response, possibly containing Location Estimate coded in GAD
- Reject
- Reset (for intra-BSS handover during TA Request)
- Abort (for inter-BSS handover)
Add encoding and decoding tests.
Change-Id: I6409c4bcac402dc7626a3afce9081c59cd715fe8
Diffstat (limited to 'src/gsm')
-rw-r--r-- | src/gsm/Makefile.am | 2 | ||||
-rw-r--r-- | src/gsm/bsslap.c | 329 | ||||
-rw-r--r-- | src/gsm/libosmogsm.map | 4 |
3 files changed, 334 insertions, 1 deletions
diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am index 4fa940be..465bae1d 100644 --- a/src/gsm/Makefile.am +++ b/src/gsm/Makefile.am @@ -33,7 +33,7 @@ libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \ 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 + gad.c bsslap.c libgsmint_la_LDFLAGS = -no-undefined libgsmint_la_LIBADD = $(top_builddir)/src/libosmocore.la diff --git a/src/gsm/bsslap.c b/src/gsm/bsslap.c new file mode 100644 index 00000000..7886da68 --- /dev/null +++ b/src/gsm/bsslap.c @@ -0,0 +1,329 @@ +/* 3GPP TS 48.071 BSSLAP protocol definitions */ +/* + * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * Author: Neels Hofmeyr <neels@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 <errno.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/bsslap.h> +#include <osmocom/gsm/tlv.h> + +#include <osmocom/core/logging.h> + +/*! \addtogroup bsslap + * @{ + * \file bsslap.c + * Message encoding and decoding for 3GPP TS 48.071 BSSLAP protocol. + */ + +static const struct tlv_definition osmo_bsslap_tlvdef = { + .def = { + [BSSLAP_IEI_TA] = { TLV_TYPE_TV }, + [BSSLAP_IEI_CELL_ID] = { TLV_TYPE_FIXED, 2 }, + [BSSLAP_IEI_CHAN_DESC] = { TLV_TYPE_FIXED, 3 }, + [BSSLAP_IEI_MEAS_REP] = { TLV_TYPE_TLV }, + [BSSLAP_IEI_CAUSE] = { TLV_TYPE_TV }, + [BSSLAP_IEI_RRLP_FLAG] = { TLV_TYPE_TV }, + [BSSLAP_IEI_RRLP] = { TLV_TYPE_TLV }, + [BSSLAP_IEI_CELL_ID_LIST] = { TLV_TYPE_TLV }, + [BSSLAP_IEI_ENH_MEAS_REP] = { TLV_TYPE_TLV }, + [BSSLAP_IEI_LAC] = { TLV_TYPE_TLV }, + [BSSLAP_IEI_FREQ_LIST] = { TLV_TYPE_TLV }, + [BSSLAP_IEI_MS_POWER] = { TLV_TYPE_TV }, + [BSSLAP_IEI_DELTA_TIMER] = { TLV_TYPE_TV }, + [BSSLAP_IEI_SERVING_CELL_ID] = { TLV_TYPE_TLV }, + [BSSLAP_IEI_ENCR_KEY] = { TLV_TYPE_FIXED, 8 }, + [BSSLAP_IEI_CIPH_MODE_SET] = { TLV_TYPE_TV }, + [BSSLAP_IEI_CHAN_MODE] = { TLV_TYPE_TV, 2 }, + [BSSLAP_IEI_MR_CONFIG] = { TLV_TYPE_TLV }, + [BSSLAP_IEI_POLLING_REPETITION] = { TLV_TYPE_TV }, + [BSSLAP_IEI_PACKET_CHAN_DESC] = { TLV_TYPE_FIXED, 4 }, + [BSSLAP_IEI_TLLI] = { TLV_TYPE_FIXED, 4 }, + [BSSLAP_IEI_TFI] = { TLV_TYPE_TLV }, + [BSSLAP_IEI_TBF_START_TIME] = { TLV_TYPE_FIXED, 2 }, + [BSSLAP_IEI_PWRUP_START_TIME] = { TLV_TYPE_TLV }, + [BSSLAP_IEI_LONG_ENCR_KEY] = { TLV_TYPE_FIXED, 16 }, + [BSSLAP_IEI_CONCUR_POS_PROC_F] = { TLV_TYPE_TV }, + }, +}; + +#define DEC_ERR(RC, MSG_TYPE, IEI, CAUSE, fmt, args...) do { \ + if (err && !*err) { \ + *err = talloc_zero(err_ctx, struct osmo_bsslap_err); \ + **err = (struct osmo_bsslap_err){ \ + .rc = (RC), \ + .msg_type = (MSG_TYPE), \ + .iei = (IEI), \ + .cause = (CAUSE), \ + .logmsg = talloc_asprintf(*err, "Error decoding BSSLAP%s%s%s%s%s: " fmt, \ + (MSG_TYPE) >= 0 ? " " : "", \ + (MSG_TYPE) >= 0 ? osmo_bsslap_msgt_name(MSG_TYPE) : "", \ + (IEI) >= 0 ? ": " : "", \ + (IEI) >= 0 ? osmo_bsslap_iei_name(IEI) : "", \ + (IEI) >= 0 ? " IE" : "", \ +##args), \ + }; \ + } \ + return RC; \ + } while(0) + +static void osmo_bsslap_ie_enc_cell_id(struct msgb *msg, uint16_t cell_id) +{ + msgb_put_u8(msg, BSSLAP_IEI_CELL_ID); + msgb_put_u16(msg, cell_id); +} + +static int osmo_bsslap_ie_dec_cell_id(uint16_t *cell_id, + enum bsslap_msgt msgt, enum bsslap_iei iei, + struct osmo_bsslap_err **err, void *err_ctx, + const uint8_t *data, size_t len) +{ + if (len != 2) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Expected 2 bytes, got %zu", len); + *cell_id = osmo_load16be(data); + return 0; +} + +static void osmo_bsslap_ie_enc_ta(struct msgb *msg, uint8_t ta) +{ + msgb_put_u8(msg, BSSLAP_IEI_TA); + msgb_put_u8(msg, ta); +} + +static int osmo_bsslap_ie_dec_ta(uint8_t *ta, + enum bsslap_msgt msgt, enum bsslap_iei iei, + struct osmo_bsslap_err **err, void *err_ctx, + const uint8_t *data, size_t len) +{ + if (len != 1) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Expected 1 byte, got %zu", len); + *ta = data[0]; + return 0; +} + +static void osmo_bsslap_ie_enc_cause(struct msgb *msg, enum bsslap_cause cause) +{ + msgb_put_u8(msg, BSSLAP_IEI_CAUSE); + msgb_put_u8(msg, cause); +} + +static int osmo_bsslap_ie_dec_cause(enum bsslap_cause *cause, + enum bsslap_msgt msgt, enum bsslap_iei iei, + struct osmo_bsslap_err **err, void *err_ctx, + const uint8_t *data, size_t len) +{ + if (len != 1) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Expected 1 byte, got %zu", len); + *cause = data[0]; + return 0; +} + +static void osmo_bsslap_ie_enc_chan_desc(struct msgb *msg, const struct gsm48_chan_desc *chan_desc) +{ + struct gsm48_chan_desc *put_chan_desc; + msgb_put_u8(msg, BSSLAP_IEI_CHAN_DESC); + put_chan_desc = (void*)msgb_put(msg, sizeof(*chan_desc)); + *put_chan_desc = *chan_desc; +} + +static int osmo_bsslap_ie_dec_chan_desc(struct gsm48_chan_desc *chan_desc, + enum bsslap_msgt msgt, enum bsslap_iei iei, + struct osmo_bsslap_err **err, void *err_ctx, + const uint8_t *data, size_t len) +{ + if (len != sizeof(*chan_desc)) + DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Expected %zu bytes, got %zu", + sizeof(*chan_desc), len); + *chan_desc = *(struct gsm48_chan_desc*)data; + return 0; +} + +/*! Encode BSSLAP PDU and append to msgb (3GPP TS 48.071). + * \param[out] msg msgb to append to. + * \param[in] pdu PDU data to encode. + * \return number of bytes written, negative on error. + */ +int osmo_bsslap_enc(struct msgb *msg, const struct bsslap_pdu *pdu) +{ + uint8_t *old_tail = msg->tail; + + msgb_put_u8(msg, pdu->msg_type); + + switch (pdu->msg_type) { + case BSSLAP_MSGT_TA_REQUEST: + /* The TA Request message contains only the message type. */ + break; + + case BSSLAP_MSGT_TA_RESPONSE: + osmo_bsslap_ie_enc_cell_id(msg, pdu->ta_response.cell_id); + osmo_bsslap_ie_enc_ta(msg, pdu->ta_response.ta); + break; + + case BSSLAP_MSGT_REJECT: + osmo_bsslap_ie_enc_cause(msg, pdu->reject); + break; + + case BSSLAP_MSGT_RESET: + osmo_bsslap_ie_enc_cell_id(msg, pdu->reset.cell_id); + osmo_bsslap_ie_enc_ta(msg, pdu->reset.ta); + osmo_bsslap_ie_enc_chan_desc(msg, &pdu->reset.chan_desc); + osmo_bsslap_ie_enc_cause(msg, pdu->reset.cause); + break; + + case BSSLAP_MSGT_ABORT: + osmo_bsslap_ie_enc_cause(msg, pdu->abort); + break; + + case BSSLAP_MSGT_TA_LAYER3: + osmo_bsslap_ie_enc_ta(msg, pdu->ta_layer3.ta); + break; + + default: + return -ENOTSUP; + } + return (msg->tail - old_tail); +} + +/*! Decode BSSLAP PDU (3GPP TS 48.071). + * \param[out] pdu Write decoded values here. + * \param[out] err Returned pointer to error info, dynamically allocated; NULL to not return any. + * \param[in] err_ctx Talloc context to allocate err from, if required. + * \param[in] data Pointer to BSSLAP PDU raw data. + * \param[in] len Data length to decode. + * \return 0 on success, negative on error. + */ +int osmo_bsslap_dec(struct bsslap_pdu *pdu, + struct osmo_bsslap_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 bsslap_pdu){}; + if (err) + *err = NULL; + +#define DEC_IE_MANDATORY(IEI, DEC_FUN, DEC_FUN_ARG) do { \ + const struct tlv_p_entry *e; \ + int rc; \ + if (!(e = TLVP_GET(&tp, IEI))) \ + DEC_ERR(-EINVAL, pdu->msg_type, IEI, LCS_CAUSE_DATA_MISSING_IN_REQ, "missing mandatory IE"); \ + rc = DEC_FUN(DEC_FUN_ARG, pdu->msg_type, IEI, err, err_ctx, e->val, e->len); \ + if (rc) \ + DEC_ERR(rc, pdu->msg_type, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \ + } while (0) + + if (len < 1) + DEC_ERR(-EINVAL, -1, -1, LCS_CAUSE_UNSPECIFIED, "PDU too short: %zu b", len); + + pdu->msg_type = data[0]; + + if (pdu->msg_type == BSSLAP_MSGT_TA_REQUEST) { + /* The TA Request message contains only the message type. */ + return 0; + } + + ies_start = &data[1]; + ies_len = len - 1; + + if (tlv_parse2(&tp, 1, &osmo_bsslap_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 BSSLAP_MSGT_TA_RESPONSE: + DEC_IE_MANDATORY(BSSLAP_IEI_CELL_ID, osmo_bsslap_ie_dec_cell_id, &pdu->ta_response.cell_id); + DEC_IE_MANDATORY(BSSLAP_IEI_TA, osmo_bsslap_ie_dec_ta, &pdu->ta_response.ta); + return 0; + + case BSSLAP_MSGT_REJECT: + DEC_IE_MANDATORY(BSSLAP_IEI_CAUSE, osmo_bsslap_ie_dec_cause, &pdu->reject); + return 0; + + case BSSLAP_MSGT_RESET: + DEC_IE_MANDATORY(BSSLAP_IEI_CELL_ID, osmo_bsslap_ie_dec_cell_id, &pdu->reset.cell_id); + DEC_IE_MANDATORY(BSSLAP_IEI_TA, osmo_bsslap_ie_dec_ta, &pdu->reset.ta); + DEC_IE_MANDATORY(BSSLAP_IEI_CHAN_DESC, osmo_bsslap_ie_dec_chan_desc, &pdu->reset.chan_desc); + DEC_IE_MANDATORY(BSSLAP_IEI_CAUSE, osmo_bsslap_ie_dec_cause, &pdu->reset.cause); + return 0; + + case BSSLAP_MSGT_ABORT: + DEC_IE_MANDATORY(BSSLAP_IEI_CAUSE, osmo_bsslap_ie_dec_cause, &pdu->abort); + return 0; + + case BSSLAP_MSGT_TA_LAYER3: + DEC_IE_MANDATORY(BSSLAP_IEI_TA, osmo_bsslap_ie_dec_ta, &pdu->ta_layer3.ta); + return 0; + + default: + DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "Unsupported message type"); + } +} + +const struct value_string osmo_bsslap_msgt_names[] = { + { BSSLAP_MSGT_TA_REQUEST, "TA Request" }, + { BSSLAP_MSGT_TA_RESPONSE, "TA Response" }, + { BSSLAP_MSGT_REJECT, "Reject" }, + { BSSLAP_MSGT_RESET, "Reset" }, + { BSSLAP_MSGT_ABORT, "Abort" }, + { BSSLAP_MSGT_TA_LAYER3, "TA Layer3" }, + { BSSLAP_MSGT_MS_POS_CMD, "MS Position Command" }, + { BSSLAP_MSGT_MS_POS_RESP, "MS Position Response" }, + { BSSLAP_MSGT_UTDOA_REQ, "U-TDOA Request" }, + { BSSLAP_MSGT_UTDOA_RESP, "U-TDOA Response" }, + {} +}; + +const struct value_string osmo_bsslap_iei_names[] = { + { BSSLAP_IEI_TA, "Timing Advance" }, + { BSSLAP_IEI_CELL_ID, "Cell Identity" }, + { BSSLAP_IEI_CHAN_DESC, "Channel Description" }, + { BSSLAP_IEI_MEAS_REP, "Measurement Report" }, + { BSSLAP_IEI_CAUSE, "Cause" }, + { BSSLAP_IEI_RRLP_FLAG, "RRLP Flag" }, + { BSSLAP_IEI_RRLP, "RRLP" }, + { BSSLAP_IEI_CELL_ID_LIST, "Cell Identity List" }, + { BSSLAP_IEI_ENH_MEAS_REP, "Enhanced Measurement Report" }, + { BSSLAP_IEI_LAC, "Location Area Code" }, + { BSSLAP_IEI_FREQ_LIST, "Frequency List" }, + { BSSLAP_IEI_MS_POWER, "MS Power" }, + { BSSLAP_IEI_DELTA_TIMER, "Delta Timer" }, + { BSSLAP_IEI_SERVING_CELL_ID, "Serving Cell Identifier" }, + { BSSLAP_IEI_ENCR_KEY, "Encryption Key" }, + { BSSLAP_IEI_CIPH_MODE_SET, "Cipher Mode Setting" }, + { BSSLAP_IEI_CHAN_MODE, "Channel Mode" }, + { BSSLAP_IEI_MR_CONFIG, "MultiRate Configuration" }, + { BSSLAP_IEI_POLLING_REPETITION, "Polling Repetition" }, + { BSSLAP_IEI_PACKET_CHAN_DESC, "Packet Channel Description" }, + { BSSLAP_IEI_TLLI, "TLLI" }, + { BSSLAP_IEI_TFI, "TFI" }, + { BSSLAP_IEI_TBF_START_TIME, "TBF Starting Time" }, + { BSSLAP_IEI_PWRUP_START_TIME, "Powerup Starting Time" }, + { BSSLAP_IEI_LONG_ENCR_KEY, "Long Encryption Key" }, + { BSSLAP_IEI_CONCUR_POS_PROC_F, "Concurrent Positioning Flag" }, + {} +}; + +/*! @} */ diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index a31f73aa..257c3fa5 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -705,6 +705,10 @@ osmo_nri_ranges_vty_del; osmo_nri_ranges_to_str_buf; osmo_nri_ranges_to_str_c; +osmo_bsslap_enc; +osmo_bsslap_dec; +osmo_bsslap_msgt_names; + osmo_gad_enc; osmo_gad_dec; osmo_gad_to_str_buf; |