add osmo_mobile_identity API

Implement better API around 3GPP TS 24.008 Mobile Identity coding.

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.

I have patches ready for current osmo CNI programs, replacing the Mobile
Identity coding with this new API. Deprecate the old MI API.
osmo-bsc: I71c3b4c65dbfdfa51409e09d4868aea83225338a
osmo-msc: Ic3f969e739654c1e8c387aedeeba5cce07fe2307
osmo-sgsn: I4cacb10bac419633ca0c14f244f9903f7f517b49
Note that some GPRS and SGs related coding is done here in libosmocore and
hence currently remains using the old implementation (see previous version of
this patch: Ic3f969e739654c1e8c387aedeeba5cce07fe2307).

New 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;
- encoding to a buffer;
- encoding to the end of a msgb.

Other than the old gsm48_generate_mid(), omit a TLV tag and length from
encoding. Many callers manually stripped the tag and value after calling
gsm48_generate_mid(). The aim is to leave writing a TL to the caller entirely,
especially since some callers need to use a TvL, i.e. support a variable-size
length of 8 or 16 bit.

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.
As a result, applications using osmo_mobile_identity will be stricter in
rejecting coding mistakes (some of which we currently have in our test suites,
and which we'll need to fix).

Rationale:

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

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.

This patch was first merged as Ic3f969e739654c1e8c387aedeeba5cce07fe2307 and
caused test fallout, because it re-implemented old API with the new stricter
decoding. In this patch version, old API remains 1:1 unchanged to avoid such
fallout. Applications will soon switch to the new osmo_mobile_identity API and
become stricter on MI coding when that happens, not implicitly by a new
libosmocore version.

Change-Id: If4f7be606e54cfa1c59084cf169785b1cbda5cf5
diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index 7c68b1d..f772f4a 100644
--- a/include/osmocom/gsm/gsm48.h
+++ b/include/osmocom/gsm/gsm48.h
@@ -4,10 +4,12 @@
 
 #include <stdbool.h>
 
+#include <osmocom/core/defs.h>
 #include <osmocom/core/msgb.h>
 
 #include <osmocom/gsm/tlv.h>
 #include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
 #include <osmocom/gsm/gsm48_ie.h>
 #include <osmocom/gsm/gsm23003.h>
 
@@ -48,16 +50,55 @@
 void gsm48_generate_lai2(struct gsm48_loc_area_id *lai48, const struct osmo_location_area_id *lai);
 
 #define GSM48_MID_MAX_SIZE	11
-int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi);
-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);
+int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi)
+	OSMO_DEPRECATED_OUTSIDE("Instead use: l = msgb_tl_put(msg, GSM48_IE_MOBILE_ID);"
+				" *l = osmo_mobile_identity_encode_msgb(...)");
+int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi)
+	OSMO_DEPRECATED_OUTSIDE("Instead use: l = msgb_tl_put(msg, GSM48_IE_MOBILE_ID);"
+				" *l = osmo_mobile_identity_encode_msgb(...)");
+uint8_t gsm48_generate_mid(uint8_t *buf, const char *id, uint8_t mi_type)
+	OSMO_DEPRECATED_OUTSIDE("Instead use: l = msgb_tl_put(msg, GSM48_IE_MOBILE_ID);"
+				" *l = osmo_mobile_identity_encode_msgb(...)");
 
-/* Convert 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);
+/* 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)
+	OSMO_DEPRECATED_OUTSIDE("Instead use osmo_mobile_identity_decode()");
+const char *osmo_mi_name(const uint8_t *mi, uint8_t mi_len)
+	OSMO_DEPRECATED_OUTSIDE("Instead use osmo_mobile_identity_to_str_c()");
+char *osmo_mi_name_buf(char *buf, size_t buf_len, const uint8_t *mi, uint8_t mi_len)
+	OSMO_DEPRECATED_OUTSIDE("Instead use osmo_mobile_identity_to_str_buf()");
+char *osmo_mi_name_c(const void *ctx, const uint8_t *mi, uint8_t mi_len)
+	OSMO_DEPRECATED_OUTSIDE("Instead use osmo_mobile_identity_to_str_c()");
+
+/*! Decoded representation of a Mobile Identity (3GPP TS 24.008 10.5.1.4).
+ * See osmo_mobile_identity_decode() and osmo_mobile_identity_from_l3(). */
+struct osmo_mobile_identity {
+	/*! A GSM_MI_TYPE_* constant (like GSM_MI_TYPE_IMSI). */
+	uint8_t type;
+	/*! Decoded Mobile Identity digits or TMSI value. IMSI, IMEI and IMEISV as digits like
+	 * "12345678", and TMSI is represented as raw uint32_t. */
+	union {
+		/*! type == GSM_MI_TYPE_IMSI. */
+		char imsi[GSM23003_IMSI_MAX_DIGITS + 1];
+		/*! type == GSM_MI_TYPE_IMEI. */
+		char imei[GSM23003_IMEI_NUM_DIGITS + 1];
+		/*! type == GSM_MI_TYPE_IMEISV. */
+		char imeisv[GSM23003_IMEISV_NUM_DIGITS + 1];
+		/*! TMSI / P-TMSI / M-TMSI integer value if type == GSM_MI_TYPE_TMSI. */
+		uint32_t tmsi;
+	};
+};
+
+int osmo_mobile_identity_to_str_buf(char *buf, size_t buflen, const struct osmo_mobile_identity *mi);
+char *osmo_mobile_identity_to_str_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,
+				bool allow_hex);
+int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct msgb *msg, bool allow_hex);
+int osmo_mobile_identity_encoded_len(const struct osmo_mobile_identity *mi, int *mi_digits);
+int osmo_mobile_identity_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_mobile_identity *mi, bool allow_hex);
+int osmo_mobile_identity_encode_msgb(struct msgb *msg, const struct osmo_mobile_identity *mi, bool allow_hex);
 
 /* Parse Routeing Area Identifier */
 void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf);