From 721aa6ded9c736e3cc5b20824dd58b1af4f4a907 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Tue, 20 Feb 2018 21:38:00 +0100 Subject: gsm: add osmo_mnc_from_str(), osmo_mnc_cmp(), osmo_plmn_cmp() for 3-digit MNC osmo_mnc_from_str() preserves leading zeros in the string and is useful for VTY config parsing (osmo-bsc, osmo-msc, osmo-sgsn, osmo-pcu). osmo_{plmn,mnc}_cmp() takes care of the slight intricacy of ignoring the 3-digit flag if the MNC is anyway >99. Will be used by osmo-sgsn.git and osmo-bsc.git. (All current users just care about identical MNC, but a proper cmp doesn't hurt.) Change-Id: Ib7176b1d65a03b76f41f94bc9d3293a8a07d24c6 --- include/osmocom/gsm/gsm23003.h | 5 +++ src/gsm/gsm23003.c | 77 +++++++++++++++++++++++++++++++++++++++++ src/gsm/libosmogsm.map | 3 ++ tests/gsm23003/gsm23003_test.c | 55 +++++++++++++++++++++++++++++ tests/gsm23003/gsm23003_test.ok | 17 +++++++++ 5 files changed, 157 insertions(+) diff --git a/include/osmocom/gsm/gsm23003.h b/include/osmocom/gsm/gsm23003.h index 51e5ef83..660186e3 100644 --- a/include/osmocom/gsm/gsm23003.h +++ b/include/osmocom/gsm/gsm23003.h @@ -95,3 +95,8 @@ const char *osmo_lai_name(const struct osmo_location_area_id *lai); void osmo_plmn_to_bcd(uint8_t *bcd_dst, const struct osmo_plmn_id *plmn); void osmo_plmn_from_bcd(const uint8_t *bcd_src, struct osmo_plmn_id *plmn); + +int osmo_mnc_from_str(const char *mnc_str, uint16_t *mnc, bool *mnc_3_digits); + +int osmo_mnc_cmp(uint16_t a_mnc, bool a_mnc_3_digits, uint16_t b_mnc, bool b_mnc_3_digits); +int osmo_plmn_cmp(const struct osmo_plmn_id *a, const struct osmo_plmn_id *b); diff --git a/src/gsm/gsm23003.c b/src/gsm/gsm23003.c index 63de2b81..1f6bf7d0 100644 --- a/src/gsm/gsm23003.c +++ b/src/gsm/gsm23003.c @@ -25,6 +25,9 @@ #include #include +#include +#include +#include #include #include @@ -195,3 +198,77 @@ void osmo_plmn_from_bcd(const uint8_t *bcd_src, struct osmo_plmn_id *plmn) plmn->mnc_3_digits = true; } } + +/* Convert string to MNC, detecting 3-digit MNC with leading zeros. + * Return mnc_3_digits as false if the MNC's most significant digit is encoded as 0xF, true + * otherwise; i.e. true if MNC > 99 or if it is represented with leading zeros instead of 0xF. + * \param mnc_str[in] String representation of an MNC, with or without leading zeros. + * \param mnc[out] MNC result buffer, or NULL. + * \param[out] mnc_3_digits Result buffer for 3-digit flag, or NULL. + * \returns zero on success, -EINVAL in case of surplus characters, negative errno in case of conversion + * errors. + */ +int osmo_mnc_from_str(const char *mnc_str, uint16_t *mnc, bool *mnc_3_digits) +{ + long int _mnc = 0; + bool _mnc_3_digits = false; + char *endptr; + int rc = 0; + + if (!mnc_str || !isdigit(mnc_str[0]) || strlen(mnc_str) > 3) { + /* return invalid, but give strtol a shot anyway, for callers that don't want to be + * strict */ + rc = -EINVAL; + } + errno = 0; + _mnc = strtol(mnc_str, &endptr, 10); + if (errno) + rc = -errno; + else if (*endptr) + rc = -EINVAL; + if (_mnc < 0 || _mnc > 999) + rc = -EINVAL; + _mnc_3_digits = strlen(mnc_str) > 2; + + if (mnc) + *mnc = (uint16_t)_mnc; + if (mnc_3_digits) + *mnc_3_digits = _mnc_3_digits; + return rc; +} + +/* Compare two MNC with three-digit flag. + * The mnc_3_digits flags passed in only have an effect if the MNC are < 100, i.e. if they would amount + * to a change in leading zeros in a BCD representation. An MNC >= 100 implies three digits, and the flag + * is actually ignored. + * \param a_mnc[in] "Left" side MNC. + * \param a_mnc_3_digits[in] "Left" side three-digits flag. + * \param b_mnc[in] "Right" side MNC. + * \param b_mnc_3_digits[in] "Right" side three-digits flag. + * \returns 0 if the MNC are equal, -1 if a < b or a shorter, 1 if a > b or a longer. */ +int osmo_mnc_cmp(uint16_t a_mnc, bool a_mnc_3_digits, uint16_t b_mnc, bool b_mnc_3_digits) +{ + if (a_mnc < b_mnc) + return -1; + if (a_mnc > b_mnc) + return 1; + /* a_mnc == b_mnc, but same amount of leading zeros? */ + if (a_mnc < 100 && a_mnc_3_digits != b_mnc_3_digits) + return a_mnc_3_digits ? 1 : -1; + return 0; +} + +/* Compare two PLMN. + * \param a[in] "Left" side PLMN. + * \param b[in] "Right" side PLMN. + * \returns 0 if the PLMN are equal, -1 if a < b or a shorter, 1 if a > b or a longer. */ +int osmo_plmn_cmp(const struct osmo_plmn_id *a, const struct osmo_plmn_id *b) +{ + if (a == b) + return 0; + if (a->mcc < b->mcc) + return -1; + if (a->mcc > b->mcc) + return 1; + return osmo_mnc_cmp(a->mnc, a->mnc_3_digits, b->mnc, b->mnc_3_digits); +} diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index 531c5c13..0a1c0c6f 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -270,6 +270,9 @@ osmo_plmn_name; osmo_plmn_name2; osmo_lai_name; osmo_rai_name; +osmo_mnc_from_str; +osmo_mnc_cmp; +osmo_plmn_cmp; gsm48_chan_mode_names; gsm_chan_t_names; gsm48_pdisc_names; diff --git a/tests/gsm23003/gsm23003_test.c b/tests/gsm23003/gsm23003_test.c index 64d756dd..4411e298 100644 --- a/tests/gsm23003/gsm23003_test.c +++ b/tests/gsm23003/gsm23003_test.c @@ -22,6 +22,8 @@ */ #include +#include +#include #include #include @@ -114,12 +116,65 @@ bool test_valid_msisdn() return pass; } +struct test_mnc_from_str_result { + int rc; + uint16_t mnc; + bool mnc_3_digits; +}; + +struct test_mnc_from_str { + const char *mnc_str; + struct test_mnc_from_str_result expect; +}; + +static struct test_mnc_from_str test_mnc_from_strs[] = { + { "0", { 0, 0, false } }, + { "00", { 0, 0, false } }, + { "000", { 0, 0, true } }, + { "1", { 0, 1, false } }, + { "01", { 0, 1, false } }, + { "001", { 0, 1, true } }, + { "", { -EINVAL, 0, false } }, + { " ", { -EINVAL, 0, false } }, + { "-1", { -EINVAL, 65535, false } }, + { "1000", { -EINVAL, 1000, true } }, + { "0x", { -EINVAL, 0, false } }, + { " 23", { -EINVAL, 23, true } }, /* technically not a 3-digit MNC, but it's EINVAL anyway */ + { "23 ", { -EINVAL, 23, true } }, + { " 023", { -EINVAL, 23, true } }, + { "023 ", { -EINVAL, 23, true } }, + { "023 ", { -EINVAL, 23, true } }, +}; + +static bool test_mnc_from_str() +{ + int i; + bool pass = true; + printf("----- %s\n", __func__); + + for (i = 0; i < ARRAY_SIZE(test_mnc_from_strs); i++) { + struct test_mnc_from_str *t = &test_mnc_from_strs[i]; + struct test_mnc_from_str_result result; + bool ok; + + result.rc = osmo_mnc_from_str(t->mnc_str, &result.mnc, + &result.mnc_3_digits); + ok = !bcmp(&result, &t->expect, sizeof(result)); + printf("%2d: \"%s\" rc=%d mnc=%u mnc_3_digits=%u %s\n", + i, osmo_escape_str(t->mnc_str, -1), result.rc, result.mnc, result.mnc_3_digits, + ok ? "pass" : "FAIL"); + pass = pass && ok; + } + return pass; +} + int main(int argc, char **argv) { bool pass = true; pass = pass && test_valid_imsi(); pass = pass && test_valid_msisdn(); + pass = pass && test_mnc_from_str(); OSMO_ASSERT(pass); diff --git a/tests/gsm23003/gsm23003_test.ok b/tests/gsm23003/gsm23003_test.ok index 7d7ffd1d..565c38cf 100644 --- a/tests/gsm23003/gsm23003_test.ok +++ b/tests/gsm23003/gsm23003_test.ok @@ -42,3 +42,20 @@ 17: expect=false result=false msisdn='123456 123456' 18: expect=false result=false msisdn='123456 123456' 19: expect=false result=false msisdn='(null)' +----- test_mnc_from_str + 0: "0" rc=0 mnc=0 mnc_3_digits=0 pass + 1: "00" rc=0 mnc=0 mnc_3_digits=0 pass + 2: "000" rc=0 mnc=0 mnc_3_digits=1 pass + 3: "1" rc=0 mnc=1 mnc_3_digits=0 pass + 4: "01" rc=0 mnc=1 mnc_3_digits=0 pass + 5: "001" rc=0 mnc=1 mnc_3_digits=1 pass + 6: "" rc=-22 mnc=0 mnc_3_digits=0 pass + 7: " " rc=-22 mnc=0 mnc_3_digits=0 pass + 8: "-1" rc=-22 mnc=65535 mnc_3_digits=0 pass + 9: "1000" rc=-22 mnc=1000 mnc_3_digits=1 pass +10: "0x" rc=-22 mnc=0 mnc_3_digits=0 pass +11: " 23" rc=-22 mnc=23 mnc_3_digits=1 pass +12: "23 " rc=-22 mnc=23 mnc_3_digits=1 pass +13: " 023" rc=-22 mnc=23 mnc_3_digits=1 pass +14: "023 " rc=-22 mnc=23 mnc_3_digits=1 pass +15: "023 " rc=-22 mnc=23 mnc_3_digits=1 pass -- cgit v1.2.3