diff --git a/include/osmocom/gsm/gsm0808_utils.h b/include/osmocom/gsm/gsm0808_utils.h index 34eae4384..8e71b43ce 100644 --- a/include/osmocom/gsm/gsm0808_utils.h +++ b/include/osmocom/gsm/gsm0808_utils.h @@ -31,20 +31,27 @@ struct sockaddr_storage; /*! (225-1)/2 is the maximum number of elements in a cell identifier list. */ #define GSM0808_CELL_ID_LIST2_MAXLEN 127 -/*! Parsed representation of a cell identifier list IE. */ +/*! Instead of this, use either struct gsm0808_cell_id or gsm0808_cell_id_list2. + * All elements contain parsed representations of the data in the corresponding IE, in host-byte order. + */ +union gsm0808_cell_id_u { + struct osmo_cell_global_id global; + struct osmo_lac_and_ci_id lac_and_ci; + uint16_t ci; + struct osmo_location_area_id lai_and_lac; + uint16_t lac; +}; + +/*! Parsed representation of Cell Identifier IE (3GPP TS 48.008 3.2.2.17) */ +struct gsm0808_cell_id { + enum CELL_IDENT id_discr; + union gsm0808_cell_id_u id; +}; + +/*! Parsed representation of a Cell Identifier List IE (3GPP TS 48.008 3.2.2.27). */ struct gsm0808_cell_id_list2 { enum CELL_IDENT id_discr; - union { - /*! - * All elements of these arrays contain parsed representations of the - * data in the corresponding IE, in host-byte order. - */ - struct osmo_cell_global_id global; - struct osmo_lac_and_ci_id lac_and_ci; - uint16_t ci; - struct osmo_location_area_id lai_and_lac; - uint16_t lac; - } id_list[GSM0808_CELL_ID_LIST2_MAXLEN]; + union gsm0808_cell_id_u id_list[GSM0808_CELL_ID_LIST2_MAXLEN]; unsigned int id_list_len; }; @@ -78,6 +85,8 @@ int gsm0808_dec_cell_id_list(struct gsm0808_cell_id_list *cil, const uint8_t *elem, uint8_t len) OSMO_DEPRECATED("use gsm0808_dec_cell_id_list2 instead"); int gsm0808_cell_id_list_add(struct gsm0808_cell_id_list2 *dst, const struct gsm0808_cell_id_list2 *src); +uint8_t gsm0808_enc_cell_id(struct msgb *msg, const struct gsm0808_cell_id *ci); +int gsm0808_dec_cell_id(struct gsm0808_cell_id *ci, const uint8_t *elem, uint8_t len); int gsm0808_chan_type_to_speech_codec(uint8_t perm_spch); int gsm0808_speech_codec_from_chan_type(struct gsm0808_speech_codec *sc, uint8_t perm_spch); diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c index 996456e1e..73d9362e9 100644 --- a/src/gsm/gsm0808_utils.c +++ b/src/gsm/gsm0808_utils.c @@ -988,6 +988,57 @@ int gsm0808_cell_id_list_add(struct gsm0808_cell_id_list2 *dst, const struct gsm return added; } +/*! Encode Cell Identifier IE (3GPP TS 48.008 3.2.2.17). + * \param[out] msg Message Buffer to which IE is to be appended + * \param[in] ci Cell ID to be encoded + * \returns number of bytes appended to \a msg */ +uint8_t gsm0808_enc_cell_id(struct msgb *msg, const struct gsm0808_cell_id *ci) +{ + uint8_t rc; + uint8_t *ie_tag; + struct gsm0808_cell_id_list2 cil = { + .id_discr = ci->id_discr, + .id_list = { ci->id }, + .id_list_len = 1, + }; + + OSMO_ASSERT(msg); + OSMO_ASSERT(ci); + + ie_tag = msg->tail; + rc = gsm0808_enc_cell_id_list2(msg, &cil); + + if (rc <= 0) + return rc; + + *ie_tag = GSM0808_IE_CELL_IDENTIFIER; + return rc; +} + +/*! Decode Cell Identifier IE (3GPP TS 48.008 3.2.2.17). + * \param[out] ci Caller-provided memory to store Cell ID. + * \param[in] elem IE value to be decoded. + * \param[in] len Length of \a elem in bytes. + * \returns number of bytes parsed; negative on error */ +int gsm0808_dec_cell_id(struct gsm0808_cell_id *ci, const uint8_t *elem, uint8_t len) +{ + struct gsm0808_cell_id_list2 cil; + int rc; + rc = gsm0808_dec_cell_id_list2(&cil, elem, len); + if (rc < 0) + return rc; + if (cil.id_discr == CELL_IDENT_BSS || cil.id_discr == CELL_IDENT_NO_CELL) { + if (cil.id_list_len != 0) + return -EINVAL; + } else { + if (cil.id_list_len != 1) + return -EINVAL; + } + ci->id_discr = cil.id_discr; + ci->id = cil.id_list[0]; + return rc; +} + /*! Convert the representation of the permitted speech codec identifier * that is used in struct gsm0808_channel_type to the speech codec * representation we use in struct gsm0808_speech_codec. diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index 4d009e043..388fcc046 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -177,6 +177,8 @@ gsm0808_enc_cell_id_list2; gsm0808_dec_cell_id_list; gsm0808_dec_cell_id_list2; gsm0808_cell_id_list_add; +gsm0808_enc_cell_id; +gsm0808_dec_cell_id; gsm0808_chan_type_to_speech_codec; gsm0808_speech_codec_from_chan_type; gsm0808_speech_codec_type_names; diff --git a/tests/gsm0808/gsm0808_test.c b/tests/gsm0808/gsm0808_test.c index f46709964..8c184dda5 100644 --- a/tests/gsm0808/gsm0808_test.c +++ b/tests/gsm0808/gsm0808_test.c @@ -1256,6 +1256,196 @@ void test_cell_id_list_add() { printf("------- %s done\n", __func__); } +#define EXPECT_ENCODED(hexstr) do { \ + const char *enc_str = msgb_hexdump(msg); \ + printf("%s: encoded: %s(rc = %u)\n", __func__, enc_str, rc_enc); \ + OSMO_ASSERT(strcmp(enc_str, hexstr " ") == 0); \ + OSMO_ASSERT(rc_enc == msg->len); \ + } while(0) + +static void test_gsm0808_enc_dec_cell_id_lac() +{ + struct gsm0808_cell_id enc_ci = { + .id_discr = CELL_IDENT_LAC, + .id.lac = 0x0124, + }; + struct gsm0808_cell_id dec_ci; + struct msgb *msg; + uint8_t rc_enc; + int rc_dec; + + memset(&dec_ci, 0xa5, sizeof(dec_ci)); + + msg = msgb_alloc(1024, "output buffer"); + rc_enc = gsm0808_enc_cell_id(msg, &enc_ci); + EXPECT_ENCODED("05 03 05 01 24"); + + rc_dec = gsm0808_dec_cell_id(&dec_ci, msg->data + 2, msg->len - 2); + OSMO_ASSERT(rc_dec == 3); + + OSMO_ASSERT(enc_ci.id_discr == dec_ci.id_discr + && enc_ci.id.lac == dec_ci.id.lac); + + msgb_free(msg); +} + +static void test_gsm0808_enc_dec_cell_id_bss() +{ + struct gsm0808_cell_id enc_ci = { + .id_discr = CELL_IDENT_BSS, + }; + struct gsm0808_cell_id dec_ci; + struct msgb *msg; + uint8_t rc_enc; + int rc_dec; + + msg = msgb_alloc(1024, "output buffer"); + rc_enc = gsm0808_enc_cell_id(msg, &enc_ci); + EXPECT_ENCODED("05 01 06"); + + rc_dec = gsm0808_dec_cell_id(&dec_ci, msg->data + 2, msg->len - 2); + OSMO_ASSERT(rc_dec == 1); + + OSMO_ASSERT(enc_ci.id_discr == dec_ci.id_discr); + + msgb_free(msg); +} + +static void test_gsm0808_enc_dec_cell_id_no_cell() +{ + struct gsm0808_cell_id enc_ci = { + .id_discr = CELL_IDENT_NO_CELL, + }; + struct gsm0808_cell_id dec_ci; + struct msgb *msg; + uint8_t rc_enc; + int rc_dec; + + msg = msgb_alloc(1024, "output buffer"); + rc_enc = gsm0808_enc_cell_id(msg, &enc_ci); + EXPECT_ENCODED("05 01 03"); + + rc_dec = gsm0808_dec_cell_id(&dec_ci, msg->data + 2, msg->len - 2); + OSMO_ASSERT(rc_dec == 1); + + OSMO_ASSERT(enc_ci.id_discr == dec_ci.id_discr); + + msgb_free(msg); +} + +static void test_gsm0808_enc_dec_cell_id_lai_and_lac() +{ + struct gsm0808_cell_id enc_ci = { + .id_discr = CELL_IDENT_LAI_AND_LAC, + .id.lai_and_lac = { + .plmn = { + .mcc = 123, + .mnc = 456, + }, + .lac = 0x2342, + }, + }; + struct gsm0808_cell_id dec_ci; + struct msgb *msg; + uint8_t rc_enc; + int rc_dec; + + msg = msgb_alloc(1024, "output buffer"); + rc_enc = gsm0808_enc_cell_id(msg, &enc_ci); + EXPECT_ENCODED("05 06 04 21 63 54 23 42"); + + memset(&dec_ci, 0xa5, sizeof(dec_ci)); + rc_dec = gsm0808_dec_cell_id(&dec_ci, msg->data + 2, msg->len - 2); + OSMO_ASSERT(rc_dec == msg->len - 2); + + OSMO_ASSERT(enc_ci.id_discr == dec_ci.id_discr + && osmo_plmn_cmp(&enc_ci.id.lai_and_lac.plmn, &dec_ci.id.lai_and_lac.plmn) == 0 + && enc_ci.id.lai_and_lac.lac == dec_ci.id.lai_and_lac.lac); + msgb_free(msg); +} + +static void test_gsm0808_enc_dec_cell_id_ci() +{ + struct gsm0808_cell_id enc_ci = { + .id_discr = CELL_IDENT_CI, + .id.ci = 0x423, + }; + struct gsm0808_cell_id dec_ci; + struct msgb *msg; + uint8_t rc_enc; + int rc_dec; + + msg = msgb_alloc(1024, "output buffer"); + rc_enc = gsm0808_enc_cell_id(msg, &enc_ci); + EXPECT_ENCODED("05 03 02 04 23"); + + rc_dec = gsm0808_dec_cell_id(&dec_ci, msg->data + 2, msg->len - 2); + OSMO_ASSERT(rc_dec == msg->len - 2); + OSMO_ASSERT(enc_ci.id_discr == dec_ci.id_discr + && enc_ci.id.ci == dec_ci.id.ci); + + msgb_free(msg); +} + +static void test_gsm0808_enc_dec_cell_id_lac_and_ci() +{ + struct gsm0808_cell_id enc_ci = { + .id_discr = CELL_IDENT_LAC_AND_CI, + .id.lac_and_ci = { + .lac = 0x423, + .ci = 0x235, + }, + }; + struct gsm0808_cell_id dec_ci; + struct msgb *msg; + uint8_t rc_enc; + int rc_dec; + + msg = msgb_alloc(1024, "output buffer"); + rc_enc = gsm0808_enc_cell_id(msg, &enc_ci); + EXPECT_ENCODED("05 05 01 04 23 02 35"); + + rc_dec = gsm0808_dec_cell_id(&dec_ci, msg->data + 2, msg->len - 2); + OSMO_ASSERT(rc_dec == msg->len - 2); + OSMO_ASSERT(enc_ci.id_discr == dec_ci.id_discr + && enc_ci.id.lac_and_ci.lac == dec_ci.id.lac_and_ci.lac + && enc_ci.id.lac_and_ci.ci == dec_ci.id.lac_and_ci.ci); + + msgb_free(msg); +} + +static void test_gsm0808_enc_dec_cell_id_global() +{ + struct gsm0808_cell_id enc_ci = { + .id_discr = CELL_IDENT_WHOLE_GLOBAL, + .id.global = { + .lai = { + .plmn = { .mcc = 123, .mnc = 456 }, + .lac = 0x2342 + }, + .cell_identity = 0x423, + } + }; + struct gsm0808_cell_id dec_ci; + struct msgb *msg; + uint8_t rc_enc; + int rc_dec; + + msg = msgb_alloc(1024, "output buffer"); + rc_enc = gsm0808_enc_cell_id(msg, &enc_ci); + EXPECT_ENCODED("05 08 00 21 63 54 23 42 04 23"); + + rc_dec = gsm0808_dec_cell_id(&dec_ci, msg->data + 2, msg->len - 2); + OSMO_ASSERT(rc_dec == msg->len - 2); + + OSMO_ASSERT(enc_ci.id_discr == dec_ci.id_discr + && osmo_plmn_cmp(&enc_ci.id.global.lai.plmn, + &dec_ci.id.global.lai.plmn) == 0 + && enc_ci.id.global.lai.lac == dec_ci.id.global.lai.lac + && enc_ci.id.global.cell_identity == dec_ci.id.global.cell_identity); + msgb_free(msg); +} + int main(int argc, char **argv) { printf("Testing generation of GSM0808 messages\n"); @@ -1287,6 +1477,7 @@ int main(int argc, char **argv) test_gsm0808_enc_dec_speech_codec_list(); test_gsm0808_enc_dec_channel_type(); test_gsm0808_enc_dec_encrypt_info(); + test_gsm0808_enc_dec_cell_id_list_lac(); test_gsm0808_enc_dec_cell_id_list_single_lac(); test_gsm0808_enc_dec_cell_id_list_multi_lac(); @@ -1298,6 +1489,14 @@ int main(int argc, char **argv) test_cell_id_list_add(); + test_gsm0808_enc_dec_cell_id_lac(); + test_gsm0808_enc_dec_cell_id_bss(); + test_gsm0808_enc_dec_cell_id_no_cell(); + test_gsm0808_enc_dec_cell_id_lai_and_lac(); + test_gsm0808_enc_dec_cell_id_ci(); + test_gsm0808_enc_dec_cell_id_lac_and_ci(); + test_gsm0808_enc_dec_cell_id_global(); + printf("Done\n"); return EXIT_SUCCESS; } diff --git a/tests/gsm0808/gsm0808_test.ok b/tests/gsm0808/gsm0808_test.ok index a049dec55..27170f275 100644 --- a/tests/gsm0808/gsm0808_test.ok +++ b/tests/gsm0808/gsm0808_test.ok @@ -104,4 +104,11 @@ cil.id_list_len = 127 gsm0808_cell_id_list_add(&cil, &cgi2a) --> rc = -28 cil.id_list_len = 127 ------- test_cell_id_list_add done +test_gsm0808_enc_dec_cell_id_lac: encoded: 05 03 05 01 24 (rc = 5) +test_gsm0808_enc_dec_cell_id_bss: encoded: 05 01 06 (rc = 3) +test_gsm0808_enc_dec_cell_id_no_cell: encoded: 05 01 03 (rc = 3) +test_gsm0808_enc_dec_cell_id_lai_and_lac: encoded: 05 06 04 21 63 54 23 42 (rc = 8) +test_gsm0808_enc_dec_cell_id_ci: encoded: 05 03 02 04 23 (rc = 5) +test_gsm0808_enc_dec_cell_id_lac_and_ci: encoded: 05 05 01 04 23 02 35 (rc = 7) +test_gsm0808_enc_dec_cell_id_global: encoded: 05 08 00 21 63 54 23 42 04 23 (rc = 10) Done