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/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok
index d343869..3e6ae1f 100644
--- a/tests/gsm0408/gsm0408_test.ok
+++ b/tests/gsm0408/gsm0408_test.ok
@@ -139,6 +139,57 @@
     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
+CM Service Reestablish Request with TMSI 0x5a42e404: rc = 0, mi = TMSI-0x5A42E404 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
+Identity Response with IMSI 901700000004620: rc = 0, mi = IMSI-901700000004620 ok
+Identity Response with IMEI 123456789012345: rc = 0, mi = IMEI-123456789012345 ok
+Identity Response with IMEISV 9876543210987654: rc = 0, mi = IMEI-SV-9876543210987654 ok
+
 BSD number encoding / decoding test
 - Running test: regular 9-digit MSISDN
   - Encoding ASCII (buffer limit=0) '123456789'...