[PATCH] libosmocore[master]: implement support for 3-digit MNC with leading zeros

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Sat Feb 24 21:24:15 UTC 2018


Hello Jenkins Builder,

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

    https://gerrit.osmocom.org/6659

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

implement support for 3-digit MNC with leading zeros

Enable representing three-digit MNC with leading zeros. The MNCs 23 and 023 are
actually different; so far we treated both as 23. Re-encode an incoming BCD or
string of 023 as it were, i.e. not dropping the leading zero as 23.

Break ABI compatibility by changing the size and ordering of struct gprs_ra_id.
Ordering is changed because the canonical oder is {Mobile Country Code, Mobile
Network Code}, so have the mcc member first. Change size (and ordering) by
inserting a flag to indicate a three-digit MNC after the mnc member. ABI
compatibility cannot be maintained, since struct gprs_ra_id is a direct member
of structs bssgp_bvc_ctx and bssgp_paging_info, and even just adding a flag to
the end would cause ABI changes of those structs.

Add new API to set and read this additional flag to preserve leading zeros:
- gsm48_mcc_mnc_to_bcd2(), gsm48_mcc_mnc_from_bcd2() after
  gsm48_mcc_mnc_to_bcd() and gsm48_mcc_mnc_from_bcd().
- gsm0808_create_layer3_aoip2() after gsm0808_create_layer3_aoip().
- gsm48_decode_lai2(), gsm48_generate_lai2() after
  gsm48_decode_lai(), gsm48_generate_lai().
- The equivalent of gsm0808_create_layer3() for preserving leading zeros is
  gsm0808_create_layer3_aoip2() with the scl argument passed as NULL; instead
  of introducing a new shim function like gsm0808_create_layer3_2(), indicate
  such in the API docs, only.
- gsm48_mnc_from_str() to encode leading zeros from string, useful for VTY
  config parsing.
- gsm48_mnc_cmp() to compare MNC values including leading zeros.
- various osmo_*_name() functions. The amount and seeming duplication of these
  may seem a bit overboard, but IMO they do make sense in this way. The root
  reason is that the use of MCC and MNC is quite diverse throughout the osmocom
  code base: in certain code, they are single uint16_t, in others they are
  embedded in structs along with lac and rac; sometimes, all should be printed
  in one go (repeatedly), in other cases (vty, ctrl) they are required singled
  out. Without these functions, the formatting kind of ("%0*u", mnc_3_digits ?
  3 : 2, mnc) would be duplicated all over our diverse repositories, or
  printing MCC-MNC or RAI would otherwise end up to be more code.

In various log output, include the leading MNC zeros.

Mark one TODO in card_fs_sim.c, I am not sure how to communicate a leading zero
to/from a SIM card FS. The focus here is on the core network / BSS.

To indicate ABI incompatibility, bump libosmogsm and libosmogb LIBVERSIONs;
adjust debian files accordingly.

Implementation choices:

- The default behavior upon zero-initialization will be the mnc_3_digits flag
  set to false, which yields exactly the previous behavior.

- I decided against packing the mnc with the mnc_3_digits field into a
  sub-struct because it would enlarge this patch; plus, with a separate flag,
  current code can decide when to start heeding leading zeros.

Change-Id: Id2240f7f518494c9df6c8bda52c0d5092f90f221
---
M debian/control
R debian/libosmogb6.install
R debian/libosmogsm9.install
M include/osmocom/gsm/gsm0808.h
M include/osmocom/gsm/gsm23003.h
M include/osmocom/gsm/gsm48.h
M src/gb/Makefile.am
M src/gb/gprs_bssgp.c
M src/gb/gprs_bssgp_vty.c
M src/gsm/Makefile.am
M src/gsm/gsm0808.c
M src/gsm/gsm48.c
M src/gsm/libosmogsm.map
M src/sim/card_fs_sim.c
14 files changed, 234 insertions(+), 40 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/59/6659/4

diff --git a/debian/control b/debian/control
index 596c3f1..d911f32 100644
--- a/debian/control
+++ b/debian/control
@@ -28,8 +28,8 @@
 Depends: libosmocodec0 (= ${binary:Version}),
          libosmocoding0 (= ${binary:Version}),
          libosmocore9 (= ${binary:Version}),
-         libosmogb5 (= ${binary:Version}),
-         libosmogsm8 (= ${binary:Version}),
+         libosmogb6 (= ${binary:Version}),
+         libosmogsm9 (= ${binary:Version}),
          libosmovty4 (= ${binary:Version}),
          libosmoctrl1 (= ${binary:Version}),
          libosmosim0 (= ${binary:Version}),
@@ -146,7 +146,7 @@
  .
  This package contains the documentation for the libosmocore library.
 
-Package: libosmogb5
+Package: libosmogb6
 Section: libs
 Architecture: any
 Multi-Arch: same
@@ -167,7 +167,7 @@
 Architecture: all
 Section: doc
 Depends: ${misc:Depends},
-         libosmogb5,
+         libosmogb6,
          libjs-jquery
 Description: Documentation for the Osmo GPRS Gb library
  This is part of the libosmocore "meta"-library. The libosmocore library
@@ -178,7 +178,7 @@
  .
  This package contains the documentation for the libosmogb library.
 
-Package: libosmogsm8
+Package: libosmogsm9
 Section: libs
 Architecture: any
 Multi-Arch: same
@@ -202,7 +202,7 @@
 Architecture: all
 Section: doc
 Depends: ${misc:Depends},
-         libosmogsm8,
+         libosmogsm9,
          libjs-jquery
 Description: Documentation for the Osmo GSM utility library
  This is part of the libosmocore "meta"-library. The libosmocore library
diff --git a/debian/libosmogb5.install b/debian/libosmogb6.install
similarity index 100%
rename from debian/libosmogb5.install
rename to debian/libosmogb6.install
diff --git a/debian/libosmogsm8.install b/debian/libosmogsm9.install
similarity index 100%
rename from debian/libosmogsm8.install
rename to debian/libosmogsm9.install
diff --git a/include/osmocom/gsm/gsm0808.h b/include/osmocom/gsm/gsm0808.h
index 3deee70..f0f8a38 100644
--- a/include/osmocom/gsm/gsm0808.h
+++ b/include/osmocom/gsm/gsm0808.h
@@ -35,6 +35,10 @@
 					uint16_t cc, int lac, uint16_t _ci,
 					const struct gsm0808_speech_codec_list
 					*scl);
+struct msgb *gsm0808_create_layer3_aoip2(const struct msgb *msg_l3, uint16_t mnc,
+					 bool mnc_3_digits,
+					 uint16_t mcc, int lac, uint16_t _ci,
+					 const struct gsm0808_speech_codec_list *scl);
 struct msgb *gsm0808_create_reset(void);
 struct msgb *gsm0808_create_reset_ack(void);
 struct msgb *gsm0808_create_clear_command(uint8_t reason);
diff --git a/include/osmocom/gsm/gsm23003.h b/include/osmocom/gsm/gsm23003.h
index dd41bc5..4738d68 100644
--- a/include/osmocom/gsm/gsm23003.h
+++ b/include/osmocom/gsm/gsm23003.h
@@ -9,6 +9,7 @@
 struct osmo_plmn_id {
 	uint16_t mcc;
 	uint16_t mnc;
+	bool mnc_3_digits;
 };
 
 /* 4.1 */
diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index 9ec5463..9477fa1 100644
--- a/include/osmocom/gsm/gsm48.h
+++ b/include/osmocom/gsm/gsm48.h
@@ -2,6 +2,8 @@
 
 #pragma once
 
+#include <stdbool.h>
+
 #include <osmocom/gsm/tlv.h>
 #include <osmocom/gsm/protocol/gsm_04_08.h>
 #include <osmocom/gsm/gsm48_ie.h>
@@ -11,11 +13,18 @@
 
 /* A parsed GPRS routing area */
 struct gprs_ra_id {
-	uint16_t	mnc;
 	uint16_t	mcc;
+	uint16_t	mnc;
+	bool		mnc_3_digits;
 	uint16_t	lac;
 	uint8_t		rac;
 };
+
+const char *osmo_mcc_name(uint16_t mcc);
+const char *osmo_mnc_name(uint16_t mnc, bool mnc_3_digits);
+const char *osmo_mcc_mnc_name(uint16_t mcc, uint16_t mnc, bool mnc_3_digits);
+const char *osmo_mcc_mnc_name2(uint16_t mcc, uint16_t mnc, bool mnc_3_digits);
+const char *osmo_rai_name(const struct gprs_ra_id *rai);
 
 extern const struct tlv_definition gsm48_att_tlvdef;
 extern const struct tlv_definition gsm48_rr_att_tlvdef;
@@ -27,8 +36,12 @@
 
 int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
 		     uint16_t *mnc, uint16_t *lac);
+int gsm48_decode_lai2(struct gsm48_loc_area_id *lai, uint16_t *mcc,
+		      uint16_t *mnc, bool *mnc_3_digits, uint16_t *lac);
 void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
 			uint16_t mnc, uint16_t lac);
+void gsm48_generate_lai2(struct gsm48_loc_area_id *lai48, uint16_t mcc,
+			 uint16_t mnc, bool mnc_3_digits, uint16_t lac);
 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);
@@ -46,4 +59,6 @@
 int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc);
 
 void gsm48_mcc_mnc_to_bcd(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc);
+void gsm48_mcc_mnc_to_bcd2(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc, bool mnc_3_digits);
 void gsm48_mcc_mnc_from_bcd(uint8_t *bcd_src, uint16_t *mcc, uint16_t *mnc);
+void gsm48_mcc_mnc_from_bcd2(uint8_t *bcd_src, uint16_t *mcc, uint16_t *mnc, bool *mnc_3_digits);
diff --git a/src/gb/Makefile.am b/src/gb/Makefile.am
index 1e0aa1e..70a451d 100644
--- a/src/gb/Makefile.am
+++ b/src/gb/Makefile.am
@@ -1,6 +1,6 @@
 # This is _NOT_ the library release version, it's an API version.
 # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification
-LIBVERSION=5:0:0
+LIBVERSION=6:0:0
 
 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
 AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} -fno-strict-aliasing $(TALLOC_CFLAGS)
diff --git a/src/gb/gprs_bssgp.c b/src/gb/gprs_bssgp.c
index 4c0bb8a..d5d4ea8 100644
--- a/src/gb/gprs_bssgp.c
+++ b/src/gb/gprs_bssgp.c
@@ -284,9 +284,8 @@
 		/* actually extract RAC / CID */
 		bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id,
 						TLVP_VAL(tp, BSSGP_IE_CELL_ID));
-		LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n",
-			bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac,
-			bctx->ra_id.rac, bctx->cell_id, bvci);
+		LOGP(DBSSGP, LOGL_NOTICE, "Cell %s CI %u on BVCI %u\n",
+		     osmo_rai_name(&bctx->ra_id), bctx->cell_id, bvci);
 	}
 
 	/* Send NM_BVC_RESET.ind to NM */
diff --git a/src/gb/gprs_bssgp_vty.c b/src/gb/gprs_bssgp_vty.c
index 6131e6b..3af6517 100644
--- a/src/gb/gprs_bssgp_vty.c
+++ b/src/gb/gprs_bssgp_vty.c
@@ -81,11 +81,9 @@
 
 static void dump_bvc(struct vty *vty, struct bssgp_bvc_ctx *bvc, int stats)
 {
-	vty_out(vty, "NSEI %5u, BVCI %5u, RA-ID: %u-%u-%u-%u, CID: %u, "
-		"STATE: %s%s", bvc->nsei, bvc->bvci, bvc->ra_id.mcc,
-		bvc->ra_id.mnc, bvc->ra_id.lac, bvc->ra_id.rac, bvc->cell_id,
-		bvc->state & BVC_S_BLOCKED ? "BLOCKED" : "UNBLOCKED",
-		VTY_NEWLINE);
+	vty_out(vty, "NSEI %5u, BVCI %5u, RA-ID: %s, CID: %u, "
+		"STATE: %s%s", bvc->nsei, bvc->bvci, osmo_rai_name(&bvc->ra_id),
+		bvc->cell_id, bvc->state & BVC_S_BLOCKED ? "BLOCKED" : "UNBLOCKED", VTY_NEWLINE);
 
 	if (stats) {
 		struct bssgp_flow_control *fc = bvc->fc;
diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am
index f85aba3..b0d6dbd 100644
--- a/src/gsm/Makefile.am
+++ b/src/gsm/Makefile.am
@@ -1,7 +1,7 @@
 # This is _NOT_ the library release version, it's an API version.
 # Please read chapter "Library interface versions" of the libtool documentation
 # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
-LIBVERSION=8:0:0
+LIBVERSION=9:0:0
 
 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
 AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN}
diff --git a/src/gsm/gsm0808.c b/src/gsm/gsm0808.c
index c0be374..cc609a3 100644
--- a/src/gsm/gsm0808.c
+++ b/src/gsm/gsm0808.c
@@ -37,7 +37,8 @@
 #define BSSMAP_MSG_SIZE 512
 #define BSSMAP_MSG_HEADROOM 128
 
-/*! Create "Complete L3 Info" for AoIP
+/*! Create "Complete L3 Info" for AoIP, legacy implementation.
+ * Instead use gsm0808_create_layer3_aoip2(), which is capable of three-digit MNC with leading zeros.
  *  \param[in] msg_l3 msgb containing Layer 3 Message
  *  \param[in] nc Mobile Network Code
  *  \param[in] cc Mobile Country Code
@@ -49,6 +50,23 @@
 					uint16_t cc, int lac, uint16_t _ci,
 					const struct gsm0808_speech_codec_list
 					*scl)
+{
+	return gsm0808_create_layer3_aoip2(msg_l3, nc, false, cc, lac, _ci, scl);
+}
+
+/*! Create "Complete L3 Info" for AoIP.
+ *  \param[in] msg_l3 msgb containing Layer 3 Message.
+ *  \param[in] mnc Mobile Network Code.
+ *  \param[in] mnc_3_digits True to indicate a three-digit MNC.
+ *  \param[in] mcc Mobile Country Code.
+ *  \param[in] lac Location Area Code.
+ *  \param[in] _ci Cell Identity
+ *  \param[in] scl Speech Codec List
+ *  \returns callee-allocated msgb with Complete L3 Info message */
+struct msgb *gsm0808_create_layer3_aoip2(const struct msgb *msg_l3, uint16_t mnc,
+					 bool mnc_3_digits,
+					 uint16_t mcc, int lac, uint16_t _ci,
+					 const struct gsm0808_speech_codec_list *scl)
 {
 	struct msgb* msg;
 	struct {
@@ -67,7 +85,7 @@
 
 	/* create the cell header */
 	lai_ci.ident = CELL_IDENT_WHOLE_GLOBAL;
-	gsm48_generate_lai(&lai_ci.lai, cc, nc, lac);
+	gsm48_generate_lai2(&lai_ci.lai, mcc, mnc, mnc_3_digits, lac);
 	lai_ci.ci = osmo_htons(_ci);
 	msgb_tlv_put(msg, GSM0808_IE_CELL_IDENTIFIER, sizeof(lai_ci),
 		     (uint8_t *) &lai_ci);
@@ -86,7 +104,9 @@
 	return msg;
 }
 
-/*! Create "Complete L3 Info" for A
+/*! Create "Complete L3 Info" for A, legacy implementation.
+ * Instead use gsm0808_create_layer3_aoip2() with the scl parameter passed as NULL,
+ * which is capable of three-digit MNC with leading zeros.
  *  \param[in] msg_l3 msgb containing Layer 3 Message
  *  \param[in] nc Mobile Network Code
  *  \param[in] cc Mobile Country Code
diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c
index b58e9e2..80a9e85 100644
--- a/src/gsm/gsm48.c
+++ b/src/gsm/gsm48.c
@@ -30,6 +30,7 @@
 #include <string.h>
 #include <stdbool.h>
 #include <inttypes.h>
+#include <ctype.h>
 
 #include <osmocom/core/utils.h>
 #include <osmocom/core/byteswap.h>
@@ -451,14 +452,27 @@
 	}
 }
 
+/* Convert MCC + MNC to BCD representation, legacy implementation.
+ * Instead use gsm48_mcc_mnc_to_bcd2(), which is also capable of converting
+ * 3-digit MNC that have leading zeros. For parameters, also see there. */
+void gsm48_mcc_mnc_to_bcd(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc)
+{
+	gsm48_mcc_mnc_to_bcd2(bcd_dst, mcc, mnc, false);
+}
+
 /* Convert MCC + MNC to BCD representation
  * \param[out] bcd_dst caller-allocated memory for output
  * \param[in] mcc Mobile Country Code
  * \param[in] mnc Mobile Network Code
+ * \param[in] mnc_3_digits true if the MNC shall have three digits.
  *
  * Convert given mcc and mnc to BCD and write to *bcd_dst, which must be an
- * allocated buffer of (at least) 3 bytes length. */
-void gsm48_mcc_mnc_to_bcd(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc)
+ * allocated buffer of (at least) 3 bytes length. Encode the MNC in three
+ * digits if its integer value is > 99, or if mnc_3_digits is passed true.
+ * Encode an MNC < 100 with mnc_3_digits passed as true as a three-digit MNC
+ * with leading zeros in the BCD representation.
+ */
+void gsm48_mcc_mnc_to_bcd2(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc, bool mnc_3_digits)
 {
 	uint8_t bcd[3];
 
@@ -467,8 +481,7 @@
 	bcd_dst[1] = bcd[2];
 
 	to_bcd(bcd, mnc);
-	/* FIXME: do we need three-digit MNC? See Table 10.5.3 */
-	if (mnc > 99) {
+	if (mnc > 99 || mnc_3_digits) {
 		bcd_dst[1] |= bcd[2] << 4;
 		bcd_dst[2] = bcd[0] | (bcd[1] << 4);
 	} else {
@@ -477,26 +490,48 @@
 	}
 }
 
-/* Convert given 3-byte BCD buffer to integers and write results to *mcc and
- * *mnc. The first three BCD digits result in the MCC and the remaining ones in
- * the MNC. */
+/* Convert given 3-byte BCD buffer to integers, legacy implementation.
+ * Instead use gsm48_mcc_mnc_from_bcd2(), which is also capable of converting
+ * 3-digit MNC that have leading zeros. For parameters, also see there. */
 void gsm48_mcc_mnc_from_bcd(uint8_t *bcd_src, uint16_t *mcc, uint16_t *mnc)
 {
-	*mcc = (bcd_src[0] & 0x0f) * 100
-	     + (bcd_src[0] >> 4) * 10
-	     + (bcd_src[1] & 0x0f);
+	gsm48_mcc_mnc_from_bcd2(bcd_src, mcc, mnc, NULL);
+}
+
+/* Convert given 3-byte BCD buffer to integers and write results to *mcc and
+ * *mnc. The first three BCD digits result in the MCC and the remaining ones in
+ * the MNC. 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[in] bcd_src 	3-byte BCD buffer containing MCC+MNC representations.
+ * \param[out] mcc 	MCC result buffer, or NULL.
+ * \param[out] mnc	MNC result buffer, or NULL.
+ * \param[out] mnc_3_digits	Result buffer for 3-digit flag, or NULL.
+ */
+void gsm48_mcc_mnc_from_bcd2(uint8_t *bcd_src, uint16_t *mcc, uint16_t *mnc, bool *mnc_3_digits)
+{
+	if (mcc)
+		*mcc = (bcd_src[0] & 0x0f) * 100
+			+ (bcd_src[0] >> 4) * 10
+			+ (bcd_src[1] & 0x0f);
 
 	if ((bcd_src[1] & 0xf0) == 0xf0) {
-		*mnc = (bcd_src[2] & 0x0f) * 10
-		     + (bcd_src[2] >> 4);
+		if (mnc)
+			*mnc = (bcd_src[2] & 0x0f) * 10
+				+ (bcd_src[2] >> 4);
+		if (mnc_3_digits)
+			*mnc_3_digits = false;
 	} else {
-		*mnc = (bcd_src[2] & 0x0f) * 100
-		     + (bcd_src[2] >> 4) * 10
-		     + (bcd_src[1] >> 4);
+		if (mnc)
+			*mnc = (bcd_src[2] & 0x0f) * 100
+				+ (bcd_src[2] >> 4) * 10
+				+ (bcd_src[1] >> 4);
+		if (mnc_3_digits)
+			*mnc_3_digits = true;
 	}
 }
 
-/*! Encode TS 04.08 Location Area Identifier
+/*! Encode TS 04.08 Location Area Identifier, legacy implementation.
+ * Instead use gsm48_generate_lai2(), which is capable of three-digit MNC with leading zeros.
  *  \param[out] caller-provided memory for output
  *  \param[in] mcc Mobile Country Code
  *  \param[in] mnc Mobile Network Code
@@ -504,11 +539,103 @@
 void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
 			uint16_t mnc, uint16_t lac)
 {
-	gsm48_mcc_mnc_to_bcd(&lai48->digits[0], mcc, mnc);
+	gsm48_generate_lai2(lai48, mcc, mnc, false, lac);
+}
+
+/*! Encode TS 04.08 Location Area Identifier.
+ *  \param[out] caller-provided memory for output.
+ *  \param[in] mcc Mobile Country Code.
+ *  \param[in] mnc Mobile Network Code.
+ *  \param[in] mnc_3_digits True to enforce an MNC with three digits.
+ *  \param[in] lac Location Area Code. */
+void gsm48_generate_lai2(struct gsm48_loc_area_id *lai48, uint16_t mcc,
+			 uint16_t mnc, bool mnc_3_digits, uint16_t lac)
+{
+	gsm48_mcc_mnc_to_bcd2(&lai48->digits[0], mcc, mnc, mnc_3_digits);
 	lai48->lac = osmo_htons(lac);
 }
 
-/*! Decode TS 04.08 Location Area Identifier
+/*! Return MCC string as standardized 3-digit with leading zeros.
+ * \param[in] mcc  MCC value.
+ * \returns string in static buffer.
+ */
+const char *osmo_mcc_name(uint16_t mcc)
+{
+	static char buf[8];
+	snprintf(buf, sizeof(buf), "%03u", mcc);
+	return buf;
+}
+
+/*! Return MNC string as standardized 2- or 3-digit with leading zeros.
+ * \param[in] mnc  MNC value.
+ * \param[in] mnc_3_digits  True if an MNC should fill three digits, only has an effect if MNC < 100.
+ * \returns string in static buffer.
+ */
+const char *osmo_mnc_name(uint16_t mnc, bool mnc_3_digits)
+{
+	static char buf[8];
+	snprintf(buf, sizeof(buf), "%0*u", mnc_3_digits ? 3 : 2, mnc);
+	return buf;
+}
+
+static inline void mcc_mnc_name(char *buf, size_t buflen, uint16_t mcc, uint16_t mnc, bool mnc_3_digits)
+{
+	snprintf(buf, buflen, "%s-%s", osmo_mcc_name(mcc), osmo_mnc_name(mnc, mnc_3_digits));
+}
+
+/*! Return MCC-MNC string as standardized 3-digit-dash-2/3-digit with leading zeros.
+ * \param[in] mcc  MCC value.
+ * \param[in] mnc  MNC value.
+ * \param[in] mnc_3_digits  True if an MNC should fill three digits, only has an effect if MNC < 100.
+ * \returns string in static buffer.
+ */
+const char *osmo_mcc_mnc_name(uint16_t mcc, uint16_t mnc, bool mnc_3_digits)
+{
+	static char buf[16];
+	mcc_mnc_name(buf, sizeof(buf), mcc, mnc, mnc_3_digits);
+	return buf;
+}
+
+/*! Same as osmo_mcc_mnc_name(), but returning in a different static buffer.
+ * \param[in] mcc  MCC value.
+ * \param[in] mnc  MNC value.
+ * \param[in] mnc_3_digits  True if an MNC should fill three digits, only has an effect if MNC < 100.
+ * \returns string in static buffer.
+ */
+const char *osmo_mcc_mnc_name2(uint16_t mcc, uint16_t mnc, bool mnc_3_digits)
+{
+	static char buf[16];
+	mcc_mnc_name(buf, sizeof(buf), mcc, mnc, mnc_3_digits);
+	return buf;
+}
+
+/*! Return MCC-MNC-LAC-RAC as string, in a static buffer.
+ * \param[in] rai  RAI to encode.
+ * \returns Static string buffer.
+ */
+const char *osmo_rai_name(const struct gprs_ra_id *rai)
+{
+	static char buf[32];
+	snprintf(buf, sizeof(buf), "%s-%s-%u-%u",
+		 osmo_mcc_name(rai->mcc), osmo_mnc_name(rai->mnc, rai->mnc_3_digits), rai->lac,
+		 rai->rac);
+	return buf;
+}
+
+/*! Return MCC-MNC-LAC as string, in a static buffer.
+ * \param[in] lai  LAI to encode, the rac member is ignored.
+ * \returns Static string buffer.
+ */
+const char *osmo_lai_name(struct gprs_ra_id *lai)
+{
+	static char buf[32];
+	snprintf(buf, sizeof(buf), "%s-%s-%u",
+		 osmo_mcc_name(lai->mcc), osmo_mnc_name(lai->mnc, lai->mnc_3_digits), lai->lac);
+	return buf;
+}
+
+/*! Decode TS 04.08 Location Area Identifier, legacy implementation.
+ * Instead use gsm48_decode_lai2(), which is capable of three-digit MNC with leading zeros.
  *  \param[in] Location Area Identifier (encoded)
  *  \param[out] mcc Mobile Country Code
  *  \param[out] mnc Mobile Network Code
@@ -519,7 +646,22 @@
 int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
 		     uint16_t *mnc, uint16_t *lac)
 {
-	gsm48_mcc_mnc_from_bcd(&lai->digits[0], mcc, mnc);
+	return gsm48_decode_lai2(lai, mcc, mnc, NULL, lac);
+}
+
+/*! Decode TS 04.08 Location Area Identifier.
+ *  \param[in] Location Area Identifier (encoded).
+ *  \param[out] mcc Mobile Country Code.
+ *  \param[out] mnc Mobile Network Code.
+ *  \param[out] mnc_3_digits True to indicate an MNC with three digits.
+ *  \param[out] lac Location Area Code.
+ *  \returns 0
+ *
+ * Attention: this function returns true integers, not hex! */
+int gsm48_decode_lai2(struct gsm48_loc_area_id *lai, uint16_t *mcc,
+		      uint16_t *mnc, bool *mnc_3_digits, uint16_t *lac)
+{
+	gsm48_mcc_mnc_from_bcd2(&lai->digits[0], mcc, mnc, mnc_3_digits);
 	*lac = osmo_ntohs(lai->lac);
 	return 0;
 }
@@ -682,10 +824,12 @@
 	if ((buf[1] >> 4) == 0xf) {
 		raid->mnc = (buf[2] & 0xf) * 10;
 		raid->mnc += (buf[2] >> 4) * 1;
+		raid->mnc_3_digits = false;
 	} else {
 		raid->mnc = (buf[2] & 0xf) * 100;
 		raid->mnc += (buf[2] >> 4) * 10;
 		raid->mnc += (buf[1] >> 4) * 1;
+		raid->mnc_3_digits = true;
 	}
 
 	raid->lac = osmo_load16be(buf + 3);
@@ -704,7 +848,7 @@
 	out->digits[0] = ((raid->mcc / 100) % 10) | (((raid->mcc / 10) % 10) << 4);
 	out->digits[1] = raid->mcc % 10;
 
-	if (raid->mnc < 100) {
+	if (raid->mnc < 100 && !raid->mnc_3_digits) {
 		out->digits[1] |= 0xf0;
 		out->digits[2] = ((raid->mnc / 10) % 10) | ((raid->mnc % 10) << 4);
 	} else {
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 7a74718..a445f0e 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -154,6 +154,7 @@
 gsm0808_create_dtap;
 gsm0808_create_layer3;
 gsm0808_create_layer3_aoip;
+gsm0808_create_layer3_aoip2;
 gsm0808_create_reset;
 gsm0808_create_reset_ack;
 gsm0808_create_sapi_reject;
@@ -222,6 +223,7 @@
 gsm48_decode_freq_list;
 gsm48_decode_keypad;
 gsm48_decode_lai;
+gsm48_decode_lai2;
 gsm48_decode_notify;
 gsm48_decode_progress;
 gsm48_decode_redirecting;
@@ -246,6 +248,7 @@
 gsm48_encode_ssversion;
 gsm48_encode_useruser;
 gsm48_generate_lai;
+gsm48_generate_lai2;
 gsm48_generate_mid;
 gsm48_generate_mid_from_imsi;
 gsm48_generate_mid_from_tmsi;
@@ -258,7 +261,16 @@
 gsm48_dtx_mode;
 gsm48_mi_type_name;
 gsm48_mcc_mnc_to_bcd;
+gsm48_mcc_mnc_to_bcd2;
 gsm48_mcc_mnc_from_bcd;
+gsm48_mcc_mnc_from_bcd2;
+gsm48_mnc_from_str;
+gsm48_mnc_cmp;
+osmo_mcc_name;
+osmo_mnc_name;
+osmo_mcc_mnc_name;
+osmo_mcc_mnc_name2;
+osmo_rai_name;
 gsm48_chan_mode_names;
 gsm_chan_t_names;
 gsm48_pdisc_names;
diff --git a/src/sim/card_fs_sim.c b/src/sim/card_fs_sim.c
index f66e391..8c819a4 100644
--- a/src/sim/card_fs_sim.c
+++ b/src/sim/card_fs_sim.c
@@ -216,6 +216,7 @@
 
 		mnc = element_alloc_sub(elem, "MNC", ELEM_T_UINT16, ELEM_REPR_DEC);
 		mnc->u.u16 = ra_id.mnc;
+		/* TODO: what about ra_id.mnc_3_digits? */
 	}
 
 	return 0;

-- 
To view, visit https://gerrit.osmocom.org/6659
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Id2240f7f518494c9df6c8bda52c0d5092f90f221
Gerrit-PatchSet: 4
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder



More information about the gerrit-log mailing list