add gsm0808_{enc,dec}_cell_id

Clarify semantics and micro-optimise for the case of single Cell Identifer IEs.
Test in gsm0808_test.c

So far we have gsm0808_enc_cell_id_list2(), but there also exist instances of
single Cell Identifiers (3GPP TS 48.008 3.2.2.17).

It is possible to decode the same using the cell identifier list API, but this
forces the caller to also keep a full struct gsm0808_cell_id_list2 with all its
127 entries around.

E.g. for handover, there are two Cell Identifiers (Serving and Target); I'd
need two full cell id lists for each, and these would be dynamically allocated
for each handover operation, whether it uses them or not.

Related: OS#2283 (inter-BSC HO, BSC side)
Change-Id: I9f9c528965775698ab62ac386af0516192c4b0cc
This commit is contained in:
Neels Hofmeyr 2018-04-13 03:30:14 +02:00
parent a78b22ba20
commit 250e7f7d30
5 changed files with 280 additions and 12 deletions

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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;
}

View File

@ -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