[PATCH] libosmocore[master]: gsm: add osmo_mnc_from_str(), osmo_mnc_cmp(), osmo_plmn_cmp(...

2018-02-27 Thread Neels Hofmeyr
Hello Harald Welte, Jenkins Builder,

I'd like you to reexamine a change.  Please visit

https://gerrit.osmocom.org/6663

to look at the new patch set (#7).

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
---
M include/osmocom/gsm/gsm23003.h
M src/gsm/gsm23003.c
M src/gsm/libosmogsm.map
M tests/gsm23003/gsm23003_test.c
M tests/gsm23003/gsm23003_test.ok
5 files changed, 157 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/63/6663/7

diff --git a/include/osmocom/gsm/gsm23003.h b/include/osmocom/gsm/gsm23003.h
index 51e5ef8..660186e 100644
--- a/include/osmocom/gsm/gsm23003.h
+++ b/include/osmocom/gsm/gsm23003.h
@@ -95,3 +95,8 @@
 
 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 63de2b8..1f6bf7d 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 @@
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_digitsResult 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, , 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 2c5b329..ffda6c2 100644
--- a/src/gsm/libosmogsm.map
+++ 

[PATCH] libosmocore[master]: gsm: add osmo_mnc_from_str(), osmo_mnc_cmp(), osmo_plmn_cmp(...

2018-02-27 Thread Neels Hofmeyr
Hello Harald Welte, Jenkins Builder,

I'd like you to reexamine a change.  Please visit

https://gerrit.osmocom.org/6663

to look at the new patch set (#6).

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
---
M include/osmocom/gsm/gsm23003.h
M src/gsm/gsm23003.c
M src/gsm/libosmogsm.map
3 files changed, 83 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/63/6663/6

diff --git a/include/osmocom/gsm/gsm23003.h b/include/osmocom/gsm/gsm23003.h
index 51e5ef8..660186e 100644
--- a/include/osmocom/gsm/gsm23003.h
+++ b/include/osmocom/gsm/gsm23003.h
@@ -95,3 +95,8 @@
 
 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 63de2b8..81ab58c 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,75 @@
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_digitsResult 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)
+{
+   uint16_t _mnc = 0;
+   bool _mnc_3_digits = false;
+   char *endptr;
+   int rc = 0;
+
+   if (!mnc_str || !isdigit(mnc_str[0])) {
+   /* 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, , 10);
+   if (errno)
+   rc = -errno;
+   else if (*endptr)
+   rc = -EINVAL;
+   _mnc_3_digits = strlen(mnc_str) > 2;
+
+   if (mnc)
+   *mnc = _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 2c5b329..ffda6c2 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -270,6 +270,9 @@
 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;
 

[PATCH] libosmocore[master]: gsm: add osmo_mnc_from_str(), osmo_mnc_cmp(), osmo_plmn_cmp(...

2018-02-26 Thread Neels Hofmeyr
Hello Harald Welte, Jenkins Builder,

I'd like you to reexamine a change.  Please visit

https://gerrit.osmocom.org/6663

to look at the new patch set (#5).

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
---
M include/osmocom/gsm/gsm48.h
M src/gsm/gsm48.c
M src/gsm/libosmogsm.map
3 files changed, 80 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/63/6663/5

diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index d3732f3..3364d53 100644
--- a/include/osmocom/gsm/gsm48.h
+++ b/include/osmocom/gsm/gsm48.h
@@ -64,3 +64,8 @@
 
 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/gsm48.c b/src/gsm/gsm48.c
index 62f9277..8d31477 100644
--- a/src/gsm/gsm48.c
+++ b/src/gsm/gsm48.c
@@ -533,6 +533,78 @@
}
 }
 
+/* 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_digitsResult 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)
+{
+   uint16_t _mnc = 0;
+   bool _mnc_3_digits = false;
+   char *endptr;
+   int rc = 0;
+
+   if (!mnc_str || !isdigit(mnc_str[0])) {
+   /* 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, , 10);
+   if (errno)
+   rc = -errno;
+   else if (*endptr)
+   rc = -EINVAL;
+   _mnc_3_digits = strlen(mnc_str) > 2;
+
+   if (mnc)
+   *mnc = _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);
+}
+
 /*! Encode TS 04.08 Location Area Identifier, legacy implementation.
  * Instead use osmo_generate_lai(), which is capable of three-digit MNC with 
leading zeros.
  *  \param[out] lai48 caller-provided memory for output
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 2c5b329..ffda6c2 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -270,6 +270,9 @@
 osmo_plmn_name2;
 osmo_lai_name;
 osmo_rai_name;
+osmo_mnc_from_str;
+osmo_mnc_cmp;

[PATCH] libosmocore[master]: gsm: add osmo_mnc_from_str(), osmo_mnc_cmp(), osmo_plmn_cmp(...

2018-02-26 Thread Neels Hofmeyr

Review at  https://gerrit.osmocom.org/6959

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

add osmo_plmn_cmp()

Change-Id: I0910aee1e6be696002f37fe8d5c308cbd2bfc10b
---
M include/osmocom/gsm/gsm48.h
M src/gsm/gsm48.c
M src/gsm/libosmogsm.map
3 files changed, 80 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/59/6959/1

diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index d3732f3..3364d53 100644
--- a/include/osmocom/gsm/gsm48.h
+++ b/include/osmocom/gsm/gsm48.h
@@ -64,3 +64,8 @@
 
 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/gsm48.c b/src/gsm/gsm48.c
index 62f9277..8d31477 100644
--- a/src/gsm/gsm48.c
+++ b/src/gsm/gsm48.c
@@ -533,6 +533,78 @@
}
 }
 
+/* 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_digitsResult 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)
+{
+   uint16_t _mnc = 0;
+   bool _mnc_3_digits = false;
+   char *endptr;
+   int rc = 0;
+
+   if (!mnc_str || !isdigit(mnc_str[0])) {
+   /* 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, , 10);
+   if (errno)
+   rc = -errno;
+   else if (*endptr)
+   rc = -EINVAL;
+   _mnc_3_digits = strlen(mnc_str) > 2;
+
+   if (mnc)
+   *mnc = _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);
+}
+
 /*! Encode TS 04.08 Location Area Identifier, legacy implementation.
  * Instead use osmo_generate_lai(), which is capable of three-digit MNC with 
leading zeros.
  *  \param[out] lai48 caller-provided memory for output
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 2c5b329..ffda6c2 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -270,6 +270,9 @@
 osmo_plmn_name2;
 osmo_lai_name;
 osmo_rai_name;
+osmo_mnc_from_str;
+osmo_mnc_cmp;
+osmo_plmn_cmp;
 gsm48_chan_mode_names;