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 all osmo programs, completely replacing the Mobile
Identity coding with this new API. Hence deprecate the old MI API.
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.
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.
Change-Id: Ic3f969e739654c1e8c387aedeeba5cce07fe2307
diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h
index e637786..8619120 100644
--- a/include/osmocom/core/utils.h
+++ b/include/osmocom/core/utils.h
@@ -55,6 +55,7 @@
uint8_t osmo_char2bcd(char c);
int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex);
+int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_nibble, int end_nibble, bool allow_hex);
int osmo_hexparse(const char *str, uint8_t *b, int max_len);
diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index 7c68b1d..81f6028 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("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("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("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("Instead use osmo_mobile_identity_decode()");
+const char *osmo_mi_name(const uint8_t *mi, uint8_t mi_len)
+ OSMO_DEPRECATED("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("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("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);