Change in libosmocore[master]: add osmo_mobile_identity API

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

neels gerrit-no-reply at lists.osmocom.org
Tue May 26 23:59:30 UTC 2020


neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmocore/+/18507 )


Change subject: add osmo_mobile_identity API
......................................................................

add osmo_mobile_identity API

While implementing osmo-bsc's MSC pooling feature, this API will be used to
reduce the number of times a Mobile Identity is extracted from a raw RSL
message.

Rationale:

Extracting the Mobile Identity from messages has numerous duplicate
implementations across our code with various levels of specialization.
https://xkcd.com/927/

To name a few:
- libosmocore: gsm48_mi_to_string(), osmo_mi_name_buf()
- osmo-bsc: extract_sub()
- osmo-msc: mm_rx_loc_upd_req(), cm_serv_reuse_conn(), gsm48_rx_mm_serv_req(),
  vlr_proc_acc_req()

We have existing functions to produce a human readable string from a Mobile
Identity, more or less awkward:
- gsm48_mi_to_string() decodes a TMSI as a decimal number. These days we use
  hexadecimal TMSI everywhere.
- osmo_mi_name_buf() decodes the BCD digits from a raw MI every time, so we'd
  need to pass around the raw message bytes. Also, osmo_mi_name_buf() has the
  wrong signature, it should return a length like snprintf().
- osmo-bsc's extract_sub() first uses gsm48_mi_to_string() which encodes the
  raw uint32_t TMSI to a string, and then calls strtoul() via
  tmsi_from_string() to code those back to a raw uint32_t.

Each of the above implementations employ their own size overflow checks, each
invoke osmo_bcd2str() and implement their own TMSI osmo_load32be() handling.
Too much code dup, let's hope that each and every one is correct.

In osmo-bsc, I am now implementing MSC pooling, and need to extract NRI bits
from a TMSI Mobile Identity. Since none of the above functions are general
enough to be re-used, I found myself again copy-pasting Mobile Identity code:
locating the MI in a 24.008 message with proper size checks, decoding MI
octets. This time I would like it to become a generally re-usable API which
might subsequently replace older code dup.

struct osmo_mobile_identity is a decoded representation of the raw Mobile
Identity, with a string representation as well as dedicated raw uint32_t TMSI.
The aim is to remove all uncertainty about decoded buffer sizes / data types.

Two API functions provide properly size-checking implementations of:
- decoding a raw MI from a bunch of MI octets;
- locating and decoding MI from a full 3GPP TS 24.008 Complete Layer 3 msgb.

New validity checks so far not implemented anywhere else:
- stricter validation of number of digits of IMSI, IMEI, IMEI-SV MI.
- stricter on filler nibbles to be 0xf.

Change-Id: Ic3f969e739654c1e8c387aedeeba5cce07fe2307
---
M include/osmocom/gsm/gsm48.h
M src/gsm/gsm48.c
M src/gsm/libosmogsm.map
M tests/gsm0408/gsm0408_test.c
M tests/gsm0408/gsm0408_test.ok
5 files changed, 706 insertions(+), 1 deletion(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/07/18507/1

diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index 7c68b1d..15c17e2 100644
--- a/include/osmocom/gsm/gsm48.h
+++ b/include/osmocom/gsm/gsm48.h
@@ -52,13 +52,31 @@
 int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi);
 uint8_t gsm48_generate_mid(uint8_t *buf, const char *id, uint8_t mi_type);
 
-/* Convert Mobile Identity (10.5.1.4) to string */
+/* Convert encoded Mobile Identity (10.5.1.4) to string */
 int gsm48_mi_to_string(char *string, int str_len, const uint8_t *mi, int mi_len);
 const char *gsm48_mi_type_name(uint8_t mi);
 const char *osmo_mi_name(const uint8_t *mi, uint8_t mi_len);
 char *osmo_mi_name_buf(char *buf, size_t buf_len, const uint8_t *mi, uint8_t mi_len);
 char *osmo_mi_name_c(const void *ctx, const uint8_t *mi, uint8_t mi_len);
 
+/*! Decoded representation of a Mobile Identity (3GPP TS 24.008 10.5.1.4).
+ * See osmo_mobile_identity_decode() and osmo_mobile_identity_from_compl_l3(). */
+struct osmo_mobile_identity {
+	/*! A GSM_MI_TYPE_* constant (like GSM_MI_TYPE_IMSI). */
+	uint8_t type;
+	/*! Decoded human readable string of the Mobile Identity digits. IMSI, IMEI and IMEISV as digits like
+	 * "12345678", and TMSI as "0x" and 8 hexadecimal digits like "0x1234ABCD". */
+	char string[GSM48_MI_SIZE];
+	/*! TMSI / P-TMSI / M-TMSI integer value if type == GSM_MI_TYPE_TMSI, or OSMO_TMSI_UNASSIGNED otherwise. */
+	uint32_t tmsi;
+};
+
+int osmo_mobile_identity_name_buf(char *buf, size_t buflen, const struct osmo_mobile_identity *mi);
+char *osmo_mobile_identity_name_c(void *ctx, const struct osmo_mobile_identity *mi);
+int osmo_mobile_identity_cmp(const struct osmo_mobile_identity *a, const struct osmo_mobile_identity *b);
+int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *mi_data, uint8_t mi_len);
+int osmo_mobile_identity_from_compl_l3(struct osmo_mobile_identity *mi, struct msgb *msg);
+
 /* Parse Routeing Area Identifier */
 void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf);
 void gsm48_encode_ra(struct gsm48_ra_id *out, const struct gprs_ra_id *raid);
diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c
index 8d0998b..084d776 100644
--- a/src/gsm/gsm48.c
+++ b/src/gsm/gsm48.c
@@ -45,6 +45,7 @@
 #include <osmocom/gsm/protocol/gsm_04_80.h>
 #include <osmocom/gsm/protocol/gsm_08_58.h>
 #include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
 
 /*! \addtogroup gsm0408
  *  @{
@@ -524,6 +525,291 @@
 	return osmo_mi_name_buf(mi_name, buf_len, mi, mi_len);
 }
 
+/*! Extract Mobile Identity from encoded bytes (3GPP TS 24.008 10.5.1.4).
+ *
+ * On failure (negative return value), mi->type == GSM_MI_TYPE_NONE, mi->string[] is all-zero and mi->tmsi ==
+ * GSM_RESERVED_TMSI.
+ *
+ * On success, mi->type reflects the decoded Mobile Identity type (GSM_MI_TYPE_IMSI, GSM_MI_TYPE_TMSI, GSM_MI_TYPE_IMEI
+ * or GSM_MI_TYPE_IMEISV).
+ *
+ * On success, mi->string always contains a human readable representation of the Mobile Identity digits: IMSI, IMEI and
+ * IMEISV as digits like "12345678", and TMSI as "0x" and 8 hexadecimal digits like "0x1234abcd".
+ *
+ * mi->tmsi contains the uint32_t TMSI value iff the extracted Mobile Identity was a TMSI, or GSM_RESERVED_TMSI
+ * otherwise.
+ *
+ * \param[out] mi  Return buffer for decoded Mobile Identity.
+ * \param[in] mi_data  The encoded Mobile Identity octets.
+ * \param[in] mi_len  Number of octets in mi_data.
+ * \returns 0 on success, negative on error: -EBADMSG = invalid length indication or invalid data,
+ *          -EINVAL = unknown Mobile Identity type.
+ */
+int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *mi_data, uint8_t mi_len)
+{
+	int rc;
+	int nibbles_len;
+
+	if (!mi_data || mi_len < 1)
+		return -EBADMSG;
+
+	nibbles_len = (mi_len - 1) * 2 + ((mi_data[0] & GSM_MI_ODD) ? 1 : 0);
+
+	*mi = (struct osmo_mobile_identity){
+		.type = mi_data[0] & GSM_MI_TYPE_MASK,
+		.tmsi = GSM_RESERVED_TMSI,
+	};
+
+	switch (mi->type) {
+	case GSM_MI_TYPE_TMSI:
+		if (nibbles_len != (GSM23003_TMSI_NUM_BYTES * 2)) {
+			rc = -EBADMSG;
+			goto return_error;
+		}
+		break;
+
+	case GSM_MI_TYPE_IMSI:
+		if (nibbles_len < GSM23003_IMSI_MIN_DIGITS || nibbles_len > GSM23003_IMSI_MAX_DIGITS) {
+			rc = -EBADMSG;
+			goto return_error;
+		}
+		break;
+
+	case GSM_MI_TYPE_IMEI:
+		if (nibbles_len != GSM23003_IMEI_NUM_DIGITS && nibbles_len != GSM23003_IMEI_NUM_DIGITS_NO_CHK) {
+			rc = -EBADMSG;
+			goto return_error;
+		}
+		break;
+
+	case GSM_MI_TYPE_IMEISV:
+		if (nibbles_len != GSM23003_IMEISV_NUM_DIGITS) {
+			rc = -EBADMSG;
+			goto return_error;
+		}
+		break;
+
+	default:
+		rc = -EINVAL;
+		goto return_error;
+	}
+
+	switch (mi->type) {
+	case GSM_MI_TYPE_TMSI:
+		/* MI is a 32bit integer TMSI. Length has been checked above. */
+		if ((mi_data[0] & 0xf0) != 0xf0) {
+			/* A TMSI always has the first nibble == 0xf */
+			rc = -EBADMSG;
+			goto return_error;
+		}
+		mi->tmsi = osmo_load32be(&mi_data[1]);
+		rc = snprintf(mi->string, sizeof(mi->string), "0x%08" PRIX32, mi->tmsi);
+		/* rc checked below */
+		break;
+
+	case GSM_MI_TYPE_IMSI:
+	case GSM_MI_TYPE_IMEI:
+	case GSM_MI_TYPE_IMEISV:
+		/* If the length is even, the last nibble (higher nibble of last octet) must be 0xf */
+		if (!(mi_data[0] & GSM_MI_ODD)
+		    && ((mi_data[mi_len - 1] & 0xf0) != 0xf0)) {
+			rc = -EBADMSG;
+			goto return_error;
+		}
+		rc = osmo_bcd2str(mi->string, sizeof(mi->string), mi_data, 1, 1 + nibbles_len, false);
+		/* rc checked below */
+		break;
+
+	default:
+		/* Already handled above, but as future bug paranoia: */
+		rc = -EINVAL;
+		goto return_error;
+	}
+
+	/* check mi->string printing rc */
+	if (rc < 1 || rc >= sizeof(mi->string)) {
+		rc = -EBADMSG;
+		goto return_error;
+	}
+	return 0;
+
+return_error:
+	*mi = (struct osmo_mobile_identity){
+		.type = GSM_MI_TYPE_NONE,
+		.tmsi = GSM_RESERVED_TMSI,
+	};
+	return rc;
+}
+
+/*! Extract Mobile Identity from a Complete Layer 3 message.
+ *
+ * Determine the Mobile Identity data and call osmo_mobile_identity_decode() to return a decoded struct
+ * osmo_mobile_identity.
+ *
+ * \param[out] mi  Return buffer for decoded Mobile Identity.
+ * \param[in] msg  The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp).
+ * \returns 0 on success, negative on error: return codes as defined in osmo_mobile_identity_decode(), or
+ *          -ENOTSUP = not a Complete Layer 3 message,
+ */
+int osmo_mobile_identity_from_compl_l3(struct osmo_mobile_identity *mi, struct msgb *msg)
+{
+	const struct gsm48_hdr *gh;
+	int8_t pdisc = 0;
+	uint8_t mtype = 0;
+	const struct gsm48_loc_upd_req *lu;
+	const uint8_t *cm2_buf;
+	uint8_t cm2_len;
+	const uint8_t *mi_start;
+	const struct gsm48_pag_resp *paging_response;
+	const uint8_t *mi_data;
+	uint8_t mi_len;
+	const struct gsm48_imsi_detach_ind *idi;
+
+	*mi = (struct osmo_mobile_identity){
+		.type = GSM_MI_TYPE_NONE,
+		.tmsi = GSM_RESERVED_TMSI,
+	};
+
+	if (msgb_l3len(msg) < sizeof(*gh))
+		return -EBADMSG;
+
+	gh = msgb_l3(msg);
+	pdisc = gsm48_hdr_pdisc(gh);
+	mtype = gsm48_hdr_msg_type(gh);
+
+	switch (pdisc) {
+	case GSM48_PDISC_MM:
+
+		switch (mtype) {
+		case GSM48_MT_MM_LOC_UPD_REQUEST:
+			/* First make sure that lu-> can be dereferenced */
+			if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu))
+				return -EBADMSG;
+
+			/* Now we know there is enough msgb data to read a lu->mi_len, so also check that */
+			lu = (struct gsm48_loc_upd_req*)gh->data;
+			if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu) + lu->mi_len)
+				return -EBADMSG;
+			mi_data = lu->mi;
+			mi_len = lu->mi_len;
+			goto got_mi;
+
+		case GSM48_MT_MM_CM_SERV_REQ:
+			/* Unfortunately in Phase1 the Classmark2 length is variable, so we cannot
+			 * just use gsm48_service_request struct, and need to parse it manually. */
+			if (msgb_l3len(msg) < sizeof(*gh) + 2)
+				return -EBADMSG;
+
+			cm2_len = gh->data[1];
+			cm2_buf = gh->data + 2;
+			goto got_cm2;
+
+		case GSM48_MT_MM_IMSI_DETACH_IND:
+			if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*idi))
+				return -EBADMSG;
+			idi = (struct gsm48_imsi_detach_ind*) gh->data;
+			mi_data = idi->mi;
+			mi_len = idi->mi_len;
+			goto got_mi;
+
+		default:
+			break;
+		}
+		break;
+
+	case GSM48_PDISC_RR:
+
+		switch (mtype) {
+		case GSM48_MT_RR_PAG_RESP:
+			if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*paging_response))
+				return -EBADMSG;
+			paging_response = (struct gsm48_pag_resp*)gh->data;
+			cm2_len = paging_response->cm2_len;
+			cm2_buf = (uint8_t*)&paging_response->cm2;
+			goto got_cm2;
+
+		default:
+			break;
+		}
+		break;
+	}
+
+	return -ENOTSUP;
+
+got_cm2:
+	/* MI (Mobile Identity) LV follows the Classmark2 */
+
+	/* There must be at least a mi_len byte after the CM2 */
+	if (cm2_buf + cm2_len + 1 > msg->tail)
+		return -EBADMSG;
+
+	mi_start = cm2_buf + cm2_len;
+	mi_len = mi_start[0];
+	mi_data = mi_start + 1;
+
+got_mi:
+	/* mi_data points at the start of the Mobile Identity coding of mi_len bytes */
+	if (mi_data + mi_len > msg->tail)
+		return -EBADMSG;
+
+	return osmo_mobile_identity_decode(mi, mi_data, mi_len);
+}
+
+/*! Return a human readable representation of a struct osmo_mobile_identity.
+ * Write a string like "IMSI-1234567", "TMSI-0x1234ABCD" or "NONE", "NULL".
+ * \param[out] buf  String buffer to write to.
+ * \param[in] buflen  sizeof(buf).
+ * \param[in] mi  Decoded Mobile Identity data.
+ * \return the strlen() of the string written when buflen is sufficiently large, like snprintf().
+ */
+int osmo_mobile_identity_name_buf(char *buf, size_t buflen, const struct osmo_mobile_identity *mi)
+{
+	if (!mi)
+		return snprintf(buf, buflen, "NULL");
+	switch (mi->type) {
+	case GSM_MI_TYPE_IMSI:
+	case GSM_MI_TYPE_IMEI:
+	case GSM_MI_TYPE_IMEISV:
+	case GSM_MI_TYPE_TMSI:
+		return snprintf(buf, buflen, "%s-%s", gsm48_mi_type_name(mi->type), mi->string);
+	default:
+		return snprintf(buf, buflen, "%s", gsm48_mi_type_name(mi->type));
+	}
+}
+
+/*! Like osmo_mobile_identity_name_buf(), but return the string in a talloc buffer.
+ * \param[in] ctx  Talloc context to allocate from.
+ * \param[in] mi  Decoded Mobile Identity data.
+ * \return a string like "IMSI-1234567", "TMSI-0x1234ABCD" or "NONE", "NULL".
+ */
+char *osmo_mobile_identity_name_c(void *ctx, const struct osmo_mobile_identity *mi)
+{
+        OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_mobile_identity_name_buf, mi)
+}
+
+/*! Compare two osmo_mobile_identity structs, returning typical cmp() result.
+ * \param[in] a  Left side osmo_mobile_identity.
+ * \param[in] b  Right side osmo_mobile_identity.
+ * \returns 0 if both are equal, -1 if a < b, 1 if a > b.
+ */
+int osmo_mobile_identity_cmp(const struct osmo_mobile_identity *a, const struct osmo_mobile_identity *b)
+{
+	int cmp;
+	if (a == b)
+		return 0;
+	if (!a)
+		return -1;
+	if (!b)
+		return 1;
+	cmp = OSMO_CMP(a->type, b->type);
+	if (cmp)
+		return cmp;
+	cmp = OSMO_CMP(a->tmsi, b->tmsi);
+	if (cmp)
+		return cmp;
+	return strncmp(a->string, b->string, sizeof(a->string));
+}
+
 /*! Checks is particular message is cipherable in A/Gb mode according to
  *         3GPP TS 24.008 § 4.7.1.2
  *  \param[in] hdr Message header
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 98cdac5..bf8c384 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -363,6 +363,11 @@
 gsm48_generate_mid_from_imsi;
 gsm48_generate_mid_from_tmsi;
 gsm48_mi_to_string;
+osmo_mobile_identity_name_buf;
+osmo_mobile_identity_name_c;
+osmo_mobile_identity_cmp;
+osmo_mobile_identity_decode;
+osmo_mobile_identity_from_compl_l3;
 gsm48_mm_att_tlvdef;
 gsm48_number_of_paging_subchannels;
 gsm48_parse_ra;
diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index 9617823..bfc8bd4 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -611,6 +611,354 @@
 	printf("\n");
 }
 
+struct msgb *msgb_from_hex(const char *label, uint16_t size, const char *hex)
+{
+	struct msgb *msg = msgb_alloc_headroom(size, 4, label);
+	OSMO_ASSERT(msg);
+	msg->l3h = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
+	return msg;
+}
+
+struct mobile_identity_tc {
+	const char *label;
+	const char *compl_l3_msg;
+	int expect_rc;
+	struct osmo_mobile_identity expect_mi;
+};
+
+/* Some Complete Layer 3 messages copied from real GSM network traces. */
+struct mobile_identity_tc mobile_identity_tests[] = {
+	{
+		.label = "LU with IMSI 901700000004620",
+		.compl_l3_msg = "050802008168000130" "089910070000006402",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMSI,
+			.string = "901700000004620",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "LU with TMSI 0x0980ad8a",
+		.compl_l3_msg = "05084262f224002a50" "05f40980ad8a",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_TMSI,
+			.string = "0x0980AD8A",
+			.tmsi = 0x0980ad8a,
+		},
+	},
+	{
+		.label = "LU with invalid MI type",
+		.compl_l3_msg = "050802008168000130" "089d10070000006402",
+		.expect_rc = -EINVAL,
+	},
+	{
+		.label = "LU with truncated IMSI MI",
+		.compl_l3_msg = "050802008168000130" "0899100700000064",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "LU with too short IMSI MI (12345)",
+		.compl_l3_msg = "050802008168000130" "03193254",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "LU with just long enough IMSI MI 123456",
+		.compl_l3_msg = "050802008168000130" "04113254f6",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMSI,
+			.string = "123456",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "LU with max length IMSI MI 123456789012345",
+		.compl_l3_msg = "050802008168000130" "081932547698103254",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMSI,
+			.string = "123456789012345",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "LU with just too long IMSI MI 1234567890123456",
+		.compl_l3_msg = "050802008168000130" "091132547698103254f6",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "LU with truncated TMSI MI",
+		.compl_l3_msg = "05084262f224002a50" "05f40980ad",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "LU with odd length TMSI",
+		.compl_l3_msg = "05084262f224002a50" "05fc0980ad8a",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "LU with too long TMSI MI",
+		.compl_l3_msg = "05084262f224002a50" "06f40980ad23",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "LU with too short TMSI",
+		.compl_l3_msg = "05084262f224002a50" "04f480ad8a",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "CM Service Request with IMSI 123456",
+		.compl_l3_msg = "052401035058a6" "04113254f6",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMSI,
+			.string = "123456",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "CM Service Request with TMSI 0x5a42e404",
+		.compl_l3_msg = "052401035058a6" "05f45a42e404",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_TMSI,
+			.string = "0x5A42E404",
+			.tmsi = 0x5a42e404,
+		},
+	},
+	{
+		.label = "CM Service Request with shorter CM2, with IMSI 123456",
+		.compl_l3_msg = "052401025058" "04113254f6",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMSI,
+			.string = "123456",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "CM Service Request with longer CM2, with IMSI 123456",
+		.compl_l3_msg = "052401055058a62342" "04113254f6",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMSI,
+			.string = "123456",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "CM Service Request with shorter CM2, with TMSI 0x00000000",
+		.compl_l3_msg = "052401025058" "05f400000000",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_TMSI,
+			.string = "0x00000000",
+			.tmsi = 0,
+		},
+	},
+	{
+		.label = "CM Service Request with invalid MI type",
+		.compl_l3_msg = "052401035058a6" "089d10070000006402",
+		.expect_rc = -EINVAL,
+	},
+	{
+		.label = "CM Service Request with truncated IMSI MI",
+		.compl_l3_msg = "052401035058a6" "0899100700000064",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "CM Service Request with truncated TMSI MI",
+		.compl_l3_msg = "0524010150" "05f40980ad",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "CM Service Request with odd length TMSI",
+		.compl_l3_msg = "052401045058a623" "05fc0980ad8a",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "CM Service Request with too long TMSI MI",
+		.compl_l3_msg = "052401035058a6" "06f40980ad23",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "CM Service Request with too short TMSI",
+		.compl_l3_msg = "052401035058a6" "04f480ad8a",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "Paging Response with IMSI 1234567",
+		.compl_l3_msg = "06270003505886" "0419325476",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMSI,
+			.string = "1234567",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "Paging Response with TMSI 0xb48883de",
+		.compl_l3_msg = "06270003505886" "05f4b48883de",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_TMSI,
+			.string = "0xB48883DE",
+			.tmsi = 0xb48883de,
+		},
+	},
+	{
+		.label = "Paging Response with TMSI, with unused nibble not 0xf",
+		.compl_l3_msg = "06270003505886" "0504b48883de",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "Paging Response with too short IMEI (1234567)",
+		.compl_l3_msg = "06270003505886" "041a325476",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "Paging Response with IMEI 123456789012345",
+		.compl_l3_msg = "06270003505886" "081a32547698103254",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMEI,
+			.string = "123456789012345",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "Paging Response with IMEI 12345678901234 (no Luhn checksum)",
+		.compl_l3_msg = "06270003505886" "0812325476981032f4",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMEI,
+			.string = "12345678901234",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "Paging Response with IMEISV 1234567890123456",
+		.compl_l3_msg = "06270003505886" "091332547698103254f6",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMEISV,
+			.string = "1234567890123456",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "Paging Response with too short IMEISV 123456789012345",
+		.compl_l3_msg = "06270003505886" "081b32547698103254",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "Paging Response with too long IMEISV 12345678901234567",
+		.compl_l3_msg = "06270003505886" "091b3254769810325476",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "Paging Response with IMSI 123456789012345 and flipped ODD bit",
+		.compl_l3_msg = "06270003505886" "081132547698103254",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "IMSI-Detach with IMSI 901700000004620",
+		.compl_l3_msg = "050130" "089910070000006402",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMSI,
+			.string = "901700000004620",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "IMSI-Detach with TMSI 0x0980ad8a",
+		.compl_l3_msg = "050130" "05f40980ad8a",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_TMSI,
+			.string = "0x0980AD8A",
+			.tmsi = 0x0980ad8a,
+		},
+	},
+	{
+		.label = "IMSI-Detach with invalid MI type",
+		.compl_l3_msg = "050130" "089d10070000006402",
+		.expect_rc = -EINVAL,
+	},
+	{
+		.label = "IMSI-Detach with truncated IMSI MI",
+		.compl_l3_msg = "050130" "0899100700000064",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "IMSI-Detach with too short IMSI MI (12345)",
+		.compl_l3_msg = "050130" "03193254",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "IMSI-Detach with just long enough IMSI MI 123456",
+		.compl_l3_msg = "050130" "04113254f6",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMSI,
+			.string = "123456",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "IMSI-Detach with max length IMSI MI 123456789012345",
+		.compl_l3_msg = "050130" "081932547698103254",
+		.expect_mi = {
+			.type = GSM_MI_TYPE_IMSI,
+			.string = "123456789012345",
+			.tmsi = GSM_RESERVED_TMSI,
+		},
+	},
+	{
+		.label = "IMSI-Detach with just too long IMSI MI 1234567890123456",
+		.compl_l3_msg = "050130" "091132547698103254f6",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "IMSI-Detach with truncated TMSI MI",
+		.compl_l3_msg = "050130" "05f40980ad",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "IMSI-Detach with odd length TMSI",
+		.compl_l3_msg = "050130" "05fc0980ad8a",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "IMSI-Detach with too long TMSI MI",
+		.compl_l3_msg = "050130" "06f40980ad23",
+		.expect_rc = -EBADMSG,
+	},
+	{
+		.label = "IMSI-Detach with too short TMSI",
+		.compl_l3_msg = "050130" "04f480ad8a",
+		.expect_rc = -EBADMSG,
+	},
+};
+
+void test_struct_mobile_identity()
+{
+	struct mobile_identity_tc *t;
+	printf("%s()\n", __func__);
+	for (t = mobile_identity_tests; (t - mobile_identity_tests) < ARRAY_SIZE(mobile_identity_tests); t++) {
+		struct osmo_mobile_identity mi;
+		struct msgb *msg;
+		int rc;
+		memset(&mi, 0xff, sizeof(mi));
+
+		msg = msgb_from_hex(t->label, 1024, t->compl_l3_msg);
+		rc = osmo_mobile_identity_from_compl_l3(&mi, msg);
+		msgb_free(msg);
+
+		printf("%s: rc = %d", t->label, rc);
+		if (!rc) {
+			printf(", mi = %s", osmo_mobile_identity_name_c(OTC_SELECT, &mi));
+		}
+
+		if (rc == t->expect_rc
+		    && ((rc != 0) || !osmo_mobile_identity_cmp(&mi, &t->expect_mi))) {
+			printf(" ok");
+		} else {
+			printf("  ERROR: Expected rc = %d", t->expect_rc);
+			if (!t->expect_rc)
+				printf(", mi = %s", osmo_mobile_identity_name_c(OTC_SELECT, &t->expect_mi));
+		}
+		printf("\n");
+	}
+	printf("\n");
+}
+
 static const struct bcd_number_test {
 	/* Human-readable test name */
 	const char *test_name;
@@ -1182,6 +1530,7 @@
 	test_mid_from_imsi();
 	test_mid_encode_decode();
 	test_mid_decode_zero_length();
+	test_struct_mobile_identity();
 	test_bcd_number_encode_decode();
 	test_ra_cap();
 	test_lai_encode_decode();
diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok
index d343869..b012cb6 100644
--- a/tests/gsm0408/gsm0408_test.ok
+++ b/tests/gsm0408/gsm0408_test.ok
@@ -139,6 +139,53 @@
     rc=1
     returned empty string
 
+test_struct_mobile_identity()
+LU with IMSI 901700000004620: rc = 0, mi = IMSI-901700000004620 ok
+LU with TMSI 0x0980ad8a: rc = 0, mi = TMSI-0x0980AD8A ok
+LU with invalid MI type: rc = -22 ok
+LU with truncated IMSI MI: rc = -74 ok
+LU with too short IMSI MI (12345): rc = -74 ok
+LU with just long enough IMSI MI 123456: rc = 0, mi = IMSI-123456 ok
+LU with max length IMSI MI 123456789012345: rc = 0, mi = IMSI-123456789012345 ok
+LU with just too long IMSI MI 1234567890123456: rc = -74 ok
+LU with truncated TMSI MI: rc = -74 ok
+LU with odd length TMSI: rc = -74 ok
+LU with too long TMSI MI: rc = -74 ok
+LU with too short TMSI: rc = -74 ok
+CM Service Request with IMSI 123456: rc = 0, mi = IMSI-123456 ok
+CM Service Request with TMSI 0x5a42e404: rc = 0, mi = TMSI-0x5A42E404 ok
+CM Service Request with shorter CM2, with IMSI 123456: rc = 0, mi = IMSI-123456 ok
+CM Service Request with longer CM2, with IMSI 123456: rc = 0, mi = IMSI-123456 ok
+CM Service Request with shorter CM2, with TMSI 0x00000000: rc = 0, mi = TMSI-0x00000000 ok
+CM Service Request with invalid MI type: rc = -22 ok
+CM Service Request with truncated IMSI MI: rc = -74 ok
+CM Service Request with truncated TMSI MI: rc = -74 ok
+CM Service Request with odd length TMSI: rc = -74 ok
+CM Service Request with too long TMSI MI: rc = -74 ok
+CM Service Request with too short TMSI: rc = -74 ok
+Paging Response with IMSI 1234567: rc = 0, mi = IMSI-1234567 ok
+Paging Response with TMSI 0xb48883de: rc = 0, mi = TMSI-0xB48883DE ok
+Paging Response with TMSI, with unused nibble not 0xf: rc = -74 ok
+Paging Response with too short IMEI (1234567): rc = -74 ok
+Paging Response with IMEI 123456789012345: rc = 0, mi = IMEI-123456789012345 ok
+Paging Response with IMEI 12345678901234 (no Luhn checksum): rc = 0, mi = IMEI-12345678901234 ok
+Paging Response with IMEISV 1234567890123456: rc = 0, mi = IMEI-SV-1234567890123456 ok
+Paging Response with too short IMEISV 123456789012345: rc = -74 ok
+Paging Response with too long IMEISV 12345678901234567: rc = -74 ok
+Paging Response with IMSI 123456789012345 and flipped ODD bit: rc = -74 ok
+IMSI-Detach with IMSI 901700000004620: rc = 0, mi = IMSI-901700000004620 ok
+IMSI-Detach with TMSI 0x0980ad8a: rc = 0, mi = TMSI-0x0980AD8A ok
+IMSI-Detach with invalid MI type: rc = -22 ok
+IMSI-Detach with truncated IMSI MI: rc = -74 ok
+IMSI-Detach with too short IMSI MI (12345): rc = -74 ok
+IMSI-Detach with just long enough IMSI MI 123456: rc = 0, mi = IMSI-123456 ok
+IMSI-Detach with max length IMSI MI 123456789012345: rc = 0, mi = IMSI-123456789012345 ok
+IMSI-Detach with just too long IMSI MI 1234567890123456: rc = -74 ok
+IMSI-Detach with truncated TMSI MI: rc = -74 ok
+IMSI-Detach with odd length TMSI: rc = -74 ok
+IMSI-Detach with too long TMSI MI: rc = -74 ok
+IMSI-Detach with too short TMSI: rc = -74 ok
+
 BSD number encoding / decoding test
 - Running test: regular 9-digit MSISDN
   - Encoding ASCII (buffer limit=0) '123456789'...

-- 
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/18507
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: Ic3f969e739654c1e8c387aedeeba5cce07fe2307
Gerrit-Change-Number: 18507
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200526/5465a324/attachment.htm>


More information about the gerrit-log mailing list