aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils.c
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2020-05-26 02:45:23 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2020-06-12 16:35:26 +0200
commitd1ceca9d48eb3d8b212f386a1ebb35d8fc612297 (patch)
treeed3b0558378654bf1699c98b766b78520dde2d97 /src/utils.c
parent0b6a8c8446497da32970b8442c68eca796518b6d (diff)
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
Diffstat (limited to 'src/utils.c')
-rw-r--r--src/utils.c59
1 files changed, 59 insertions, 0 deletions
diff --git a/src/utils.c b/src/utils.c
index 18e105f8..3c4a8c9f 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -175,6 +175,65 @@ int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibbl
return OSMO_MAX(0, end_nibble - start_nibble);
}
+/*! Convert string to BCD.
+ * The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd[0] & 0x0f, nibble 1 is bcd[0] & 0xf0, nibble
+ * 3 is bcd[1] & 0x0f, etc..
+ * \param[out] dst Output BCD buffer.
+ * \param[in] dst_size sizeof() the output string buffer.
+ * \param[in] digits String containing decimal or hexadecimal digits in upper or lower case.
+ * \param[in] start_nibble Offset to start from, in nibbles, typically 1 to skip the first (MI type) nibble.
+ * \param[in] end_nibble Negative to write all digits found in str, followed by 0xf nibbles to fill any started octet.
+ * If >= 0, stop before this offset in nibbles, e.g. to get default behavior, pass
+ * start_nibble + strlen(str) + ((start_nibble + strlen(str)) & 1? 1 : 0) + 1.
+ * \param[in] allow_hex If false, return error if there are hexadecimal digits (A-F). If true, write those to
+ * BCD.
+ * \returns The buffer size in octets that is used to place all bcd digits (including the skipped nibbles
+ * from 'start_nibble' and rounded up to full octets); -EINVAL on invalid digits;
+ * -ENOMEM if dst is NULL, if dst_size is too small to contain all nibbles, or if start_nibble is negative.
+ */
+int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_nibble, int end_nibble, bool allow_hex)
+{
+ const char *digit = digits;
+ int nibble_i;
+
+ if (!dst || !dst_size || start_nibble < 0)
+ return -ENOMEM;
+
+ if (end_nibble < 0) {
+ end_nibble = start_nibble + strlen(digits);
+ /* If the last octet is not complete, add another filler nibble */
+ if (end_nibble & 1)
+ end_nibble++;
+ }
+ if ((end_nibble / 2) > dst_size)
+ return -ENOMEM;
+
+ for (nibble_i = start_nibble; nibble_i < end_nibble; nibble_i++) {
+ uint8_t nibble = 0xf;
+ int octet = nibble_i >> 1;
+ if (*digit) {
+ char c = *digit;
+ digit++;
+ if (c >= '0' && c <= '9')
+ nibble = c - '0';
+ else if (allow_hex && c >= 'A' && c <= 'F')
+ nibble = 0xa + (c - 'A');
+ else if (allow_hex && c >= 'a' && c <= 'f')
+ nibble = 0xa + (c - 'a');
+ else
+ return -EINVAL;
+ }
+ nibble &= 0xf;
+ if ((nibble_i & 1))
+ dst[octet] = (nibble << 4) | (dst[octet] & 0x0f);
+ else
+ dst[octet] = (dst[octet] & 0xf0) | nibble;
+ }
+
+ /* floor(float(end_nibble) / 2) */
+ return end_nibble / 2;
+}
+
/*! Parse a string containing hexadecimal digits
* \param[in] str string containing ASCII encoded hexadecimal digits
* \param[out] b output buffer