[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
Tue Feb 27 02:46:19 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 (#5).

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 structs
gprs_ra_id, osmo_plmn_id, osmo_cell_global_id, ... by adding an mnc_3_digits
flag.

Change ordering in gprs_ra_id because the canonical oder is {Mobile Country
Code, Mobile Network Code}, so have the mcc member first.

ABI compatibility cannot be maintained for struct gprs_ra_id, since it 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. Similarly,
osmo_plmn_id is a direct member of osmo_location_area_id, and so forth.

Add new API to set and read this additional flag to preserve leading zeros:
- osmo_plmn_to_bcd(), osmo_plmn_from_bcd() after
  gsm48_mcc_mnc_to_bcd() and gsm48_mcc_mnc_from_bcd().
- gsm0808_create_layer3_aoip2() after gsm0808_create_layer3_aoip().
- osmo_decode_lai(), osmo_generate_lai() after
  gsm48_decode_lai(), gsm48_generate_lai().
- gsm0808_create_layer3_2() after gsm0808_create_layer3() and gsm0808_create_layer3_aoip().
- various osmo_*_name() functions. The amount and 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 ("%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 immediately break all builds of dependent
  projects: it would require immediate merging of numerous patches in other
  repositories, and it would make compiling older code against a newer
  libosmocore unneccessarily hard.

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, 246 insertions(+), 45 deletions(-)


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

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..8c276f5 100644
--- a/include/osmocom/gsm/gsm0808.h
+++ b/include/osmocom/gsm/gsm0808.h
@@ -25,6 +25,8 @@
 
 #include "tlv.h"
 #include <osmocom/gsm/protocol/gsm_08_08.h>
+#include <osmocom/gsm/gsm23003.h>
+
 struct sockaddr_storage;
 
 struct msgb;
@@ -35,6 +37,8 @@
 					uint16_t cc, int lac, uint16_t _ci,
 					const struct gsm0808_speech_codec_list
 					*scl);
+struct msgb *gsm0808_create_layer3_2(const struct msgb *msg_l3, const struct osmo_cell_global_id *cell,
+				     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..3f36c71 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; /*< ignored and implied true if mnc > 99, otherwise defines leading zeros. */
 };
 
 /* 4.1 */
diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index 9ec5463..d3732f3 100644
--- a/include/osmocom/gsm/gsm48.h
+++ b/include/osmocom/gsm/gsm48.h
@@ -2,20 +2,31 @@
 
 #pragma once
 
+#include <stdbool.h>
+
 #include <osmocom/gsm/tlv.h>
 #include <osmocom/gsm/protocol/gsm_04_08.h>
 #include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/gsm23003.h>
 
 /* reserved according to GSM 03.03 § 2.4 */
 #define GSM_RESERVED_TMSI   0xFFFFFFFF
 
 /* 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_plmn_name(const struct osmo_plmn_id *plmn);
+const char *osmo_plmn_name2(const struct osmo_plmn_id *plmn);
+const char *osmo_lai_name(const struct osmo_location_area_id *lai);
+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;
@@ -29,6 +40,9 @@
 		     uint16_t *mnc, uint16_t *lac);
 void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
 			uint16_t mnc, uint16_t lac);
+void osmo_decode_lai(const struct gsm48_loc_area_id *lai, struct osmo_location_area_id *decoded);
+void osmo_generate_lai(struct gsm48_loc_area_id *lai48, const struct osmo_location_area_id *lai);
+
 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);
@@ -47,3 +61,6 @@
 
 void gsm48_mcc_mnc_to_bcd(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc);
 void gsm48_mcc_mnc_from_bcd(uint8_t *bcd_src, uint16_t *mcc, uint16_t *mnc);
+
+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);
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..31501c3 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,27 @@
 					uint16_t cc, int lac, uint16_t _ci,
 					const struct gsm0808_speech_codec_list
 					*scl)
+{
+	struct osmo_cell_global_id cgi = {
+		.lai = {
+			.plmn = {
+				.mcc = cc,
+				.mnc = nc,
+			},
+			.lac = lac,
+		},
+		.cell_identity = _ci,
+	};
+	return gsm0808_create_layer3_2(msg_l3, &cgi, scl);
+}
+
+/*! Create "Complete L3 Info" for AoIP.
+ *  \param[in] msg_l3 msgb containing Layer 3 Message -- not modified by this call.
+ *  \param[in] cell  MCC, MNC, LAC, CI to identify the cell.
+ *  \param[in] scl Speech Codec List, optional.
+ *  \returns newly allocated msgb with Complete L3 Info message */
+struct msgb *gsm0808_create_layer3_2(const struct msgb *msg_l3, const struct osmo_cell_global_id *cell,
+				     const struct gsm0808_speech_codec_list *scl)
 {
 	struct msgb* msg;
 	struct {
@@ -67,8 +89,8 @@
 
 	/* create the cell header */
 	lai_ci.ident = CELL_IDENT_WHOLE_GLOBAL;
-	gsm48_generate_lai(&lai_ci.lai, cc, nc, lac);
-	lai_ci.ci = osmo_htons(_ci);
+	osmo_generate_lai(&lai_ci.lai, &cell->lai);
+	lai_ci.ci = osmo_htons(cell->cell_identity);
 	msgb_tlv_put(msg, GSM0808_IE_CELL_IDENTIFIER, sizeof(lai_ci),
 		     (uint8_t *) &lai_ci);
 
@@ -86,7 +108,9 @@
 	return msg;
 }
 
-/*! Create "Complete L3 Info" for A
+/*! Create "Complete L3 Info" for A, legacy implementation.
+ * Instead use gsm0808_create_layer3_2() 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..62f9277 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,24 +452,52 @@
 	}
 }
 
+/* Convert MCC + MNC to BCD representation, legacy implementation.
+ * Instead use osmo_plmn_to_bcd(), 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)
+{
+	const struct osmo_plmn_id plmn = {
+		.mcc = mcc,
+		.mnc = mnc,
+		.mnc_3_digits = false,
+	};
+	osmo_plmn_to_bcd(bcd_dst, &plmn);
+}
+
+/* Convert given 3-byte BCD buffer to integers, legacy implementation.
+ * Instead use osmo_plmn_from_bcd(), 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)
+{
+	struct osmo_plmn_id plmn;
+	osmo_plmn_from_bcd(bcd_src, &plmn);
+	*mcc = plmn.mcc;
+	*mnc = plmn.mnc;
+}
+
 /* 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 osmo_plmn_to_bcd(uint8_t *bcd_dst, const struct osmo_plmn_id *plmn)
 {
 	uint8_t bcd[3];
 
-	to_bcd(bcd, mcc);
+	to_bcd(bcd, plmn->mcc);
 	bcd_dst[0] = bcd[0] | (bcd[1] << 4);
 	bcd_dst[1] = bcd[2];
 
-	to_bcd(bcd, mnc);
-	/* FIXME: do we need three-digit MNC? See Table 10.5.3 */
-	if (mnc > 99) {
+	to_bcd(bcd, plmn->mnc);
+	if (plmn->mnc > 99 || plmn->mnc_3_digits) {
 		bcd_dst[1] |= bcd[2] << 4;
 		bcd_dst[2] = bcd[0] | (bcd[1] << 4);
 	} else {
@@ -479,36 +508,137 @@
 
 /* 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. */
-void gsm48_mcc_mnc_from_bcd(uint8_t *bcd_src, uint16_t *mcc, uint16_t *mnc)
+ * 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 osmo_plmn_from_bcd(const uint8_t *bcd_src, struct osmo_plmn_id *plmn)
 {
-	*mcc = (bcd_src[0] & 0x0f) * 100
-	     + (bcd_src[0] >> 4) * 10
-	     + (bcd_src[1] & 0x0f);
+	plmn->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);
+		plmn->mnc = (bcd_src[2] & 0x0f) * 10
+			  + (bcd_src[2] >> 4);
+		plmn->mnc_3_digits = false;
 	} else {
-		*mnc = (bcd_src[2] & 0x0f) * 100
-		     + (bcd_src[2] >> 4) * 10
-		     + (bcd_src[1] >> 4);
+		plmn->mnc = (bcd_src[2] & 0x0f) * 100
+			  + (bcd_src[2] >> 4) * 10
+			  + (bcd_src[1] >> 4);
+		plmn->mnc_3_digits = true;
 	}
 }
 
-/*! Encode TS 04.08 Location Area Identifier
- *  \param[out] caller-provided memory for output
+/*! 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
  *  \param[in] mcc Mobile Country Code
  *  \param[in] mnc Mobile Network Code
  *  \param[in] lac Location Area Code */
 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);
-	lai48->lac = osmo_htons(lac);
+	const struct osmo_location_area_id lai = {
+		.plmn = {
+			.mcc = mcc,
+			.mnc = mnc,
+			.mnc_3_digits = false,
+		},
+		.lac = lac,
+	};
+	osmo_generate_lai(lai48, &lai);
 }
 
-/*! Decode TS 04.08 Location Area Identifier
+/*! Encode TS 04.08 Location Area Identifier.
+ *  \param[out] lai48 caller-provided memory for output.
+ *  \param[in] lai input of MCC-MNC-LAC. */
+void osmo_generate_lai(struct gsm48_loc_area_id *lai48, const struct osmo_location_area_id *lai)
+{
+	osmo_plmn_to_bcd(&lai48->digits[0], &lai->plmn);
+	lai48->lac = osmo_htons(lai->lac);
+}
+
+/*! 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 plmn_name(char *buf, size_t buflen, const struct osmo_plmn_id *plmn)
+{
+	snprintf(buf, buflen, "%s-%s", osmo_mcc_name(plmn->mcc),
+		 osmo_mnc_name(plmn->mnc, plmn->mnc_3_digits));
+}
+
+/*! Return MCC-MNC string as standardized 3-digit-dash-2/3-digit with leading zeros.
+ * \param[in] plmn  MCC-MNC value.
+ * \returns string in static buffer.
+ */
+const char *osmo_plmn_name(const struct osmo_plmn_id *plmn)
+{
+	static char buf[16];
+	plmn_name(buf, sizeof(buf), plmn);
+	return buf;
+}
+
+/*! Same as osmo_mcc_mnc_name(), but returning in a different static buffer.
+ * \param[in] plmn  MCC-MNC value.
+ * \returns string in static buffer.
+ */
+const char *osmo_plmn_name2(const struct osmo_plmn_id *plmn)
+{
+	static char buf[16];
+	plmn_name(buf, sizeof(buf), plmn);
+	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(const struct osmo_location_area_id *lai)
+{
+	static char buf[32];
+	snprintf(buf, sizeof(buf), "%s-%u", osmo_plmn_name(&lai->plmn), lai->lac);
+	return buf;
+}
+
+/*! Decode TS 04.08 Location Area Identifier, legacy implementation.
+ * Instead use osmo_decode_lai(), 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,9 +649,23 @@
 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);
-	*lac = osmo_ntohs(lai->lac);
+	struct osmo_location_area_id decoded;
+	osmo_decode_lai(lai, &decoded);
+	*mcc = decoded.plmn.mcc;
+	*mnc = decoded.plmn.mnc;
+	*lac = decoded.lac;
 	return 0;
+}
+
+/*! Decode TS 04.08 Location Area Identifier.
+ *  \param[in] Location Area Identifier (encoded).
+ *  \param[out] decoded Target buffer to write decoded values of MCC-MNC-LAC.
+ *
+ * Attention: this function returns true integers, not hex! */
+void osmo_decode_lai(const struct gsm48_loc_area_id *lai, struct osmo_location_area_id *decoded)
+{
+	osmo_plmn_from_bcd(&lai->digits[0], &decoded->plmn);
+	decoded->lac = osmo_ntohs(lai->lac);
 }
 
 /*! Set DTX mode in Cell Options IE (3GPP TS 44.018)
@@ -682,10 +826,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 +850,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..2c5b329 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_2;
 gsm0808_create_reset;
 gsm0808_create_reset_ack;
 gsm0808_create_sapi_reject;
@@ -259,6 +260,16 @@
 gsm48_mi_type_name;
 gsm48_mcc_mnc_to_bcd;
 gsm48_mcc_mnc_from_bcd;
+osmo_generate_lai;
+osmo_decode_lai;
+osmo_plmn_to_bcd;
+osmo_plmn_from_bcd;
+osmo_mcc_name;
+osmo_mnc_name;
+osmo_plmn_name;
+osmo_plmn_name2;
+osmo_lai_name;
+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: 5
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr at sysmocom.de>



More information about the gerrit-log mailing list