diff options
author | Neels Hofmeyr <neels@hofmeyr.de> | 2018-02-20 13:47:08 +0100 |
---|---|---|
committer | Neels Hofmeyr <neels@hofmeyr.de> | 2018-02-28 19:26:33 +0100 |
commit | c4fce1425e19d604c199c895e227dc2519110456 (patch) | |
tree | 0eecba6800e7ac3a714d8a7b7183b6c44bb09f40 | |
parent | 8fd85578bc7174155e1bb9e98a0d7cdbf85749af (diff) |
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().
- gsm48_decode_lai2(), gsm48_generate_lai2() after
gsm48_decode_lai(), gsm48_generate_lai().
- gsm0808_create_layer3_2() after gsm0808_create_layer3() and gsm0808_create_layer3_aoip().
- various osmo_*_name() functions in gsm23003.h (osmo_rai_name() still in
gsm48.h close to struct gprs_ra_id definition). The amount and duplication of
these may seem a bit overboard, but IMO they do make sense in this way.
Though most code will soon see patches unifying the data structures used, in
some 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.
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
-rw-r--r-- | debian/control | 12 | ||||
-rw-r--r-- | debian/libosmogb6.install (renamed from debian/libosmogb5.install) | 0 | ||||
-rw-r--r-- | debian/libosmogsm9.install (renamed from debian/libosmogsm8.install) | 0 | ||||
-rw-r--r-- | include/osmocom/gsm/gsm0808.h | 4 | ||||
-rw-r--r-- | include/osmocom/gsm/gsm23003.h | 10 | ||||
-rw-r--r-- | include/osmocom/gsm/gsm48.h | 10 | ||||
-rw-r--r-- | src/gb/Makefile.am | 2 | ||||
-rw-r--r-- | src/gb/gprs_bssgp.c | 5 | ||||
-rw-r--r-- | src/gb/gprs_bssgp_vty.c | 8 | ||||
-rw-r--r-- | src/gsm/Makefile.am | 2 | ||||
-rw-r--r-- | src/gsm/gsm0808.c | 32 | ||||
-rw-r--r-- | src/gsm/gsm23003.c | 129 | ||||
-rw-r--r-- | src/gsm/gsm48.c | 126 | ||||
-rw-r--r-- | src/gsm/libosmogsm.map | 11 | ||||
-rw-r--r-- | src/sim/card_fs_sim.c | 1 |
15 files changed, 277 insertions, 75 deletions
diff --git a/debian/control b/debian/control index 596c3f19..d911f32f 100644 --- a/debian/control +++ b/debian/control @@ -28,8 +28,8 @@ Multi-Arch: foreign 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 @@ Description: Documentation for the Osmo Core library . This package contains the documentation for the libosmocore library. -Package: libosmogb5 +Package: libosmogb6 Section: libs Architecture: any Multi-Arch: same @@ -167,7 +167,7 @@ Package: libosmogb-doc 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 @@ Description: Documentation for the Osmo GPRS Gb library . This package contains the documentation for the libosmogb library. -Package: libosmogsm8 +Package: libosmogsm9 Section: libs Architecture: any Multi-Arch: same @@ -202,7 +202,7 @@ Package: libosmogsm-doc 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 index 4c474255..4c474255 100644 --- a/debian/libosmogb5.install +++ b/debian/libosmogb6.install diff --git a/debian/libosmogsm8.install b/debian/libosmogsm9.install index 5e617298..5e617298 100644 --- a/debian/libosmogsm8.install +++ b/debian/libosmogsm9.install diff --git a/include/osmocom/gsm/gsm0808.h b/include/osmocom/gsm/gsm0808.h index 3deee70d..8c276f58 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 @@ struct msgb *gsm0808_create_layer3_aoip(const struct msgb *msg_l3, uint16_t nc, 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 dd41bc5b..51e5ef83 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 */ @@ -85,3 +86,12 @@ struct osmo_guti { bool osmo_imsi_str_valid(const char *imsi); bool osmo_msisdn_str_valid(const char *msisdn); + +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); + +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/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h index 9ec54639..5f144358 100644 --- a/include/osmocom/gsm/gsm48.h +++ b/include/osmocom/gsm/gsm48.h @@ -2,17 +2,21 @@ #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; }; @@ -24,11 +28,15 @@ const char *gsm48_cc_state_name(uint8_t state); const char *gsm48_cc_msg_name(uint8_t msgtype); const char *gsm48_rr_msg_name(uint8_t msgtype); const char *rr_cause_name(uint8_t cause); +const char *osmo_rai_name(const struct gprs_ra_id *rai); int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc, uint16_t *mnc, uint16_t *lac); +void gsm48_decode_lai2(const struct gsm48_loc_area_id *lai, struct osmo_location_area_id *decoded); 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, 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); diff --git a/src/gb/Makefile.am b/src/gb/Makefile.am index 1e0aa1e1..70a451d2 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 4c0bb8a9..d5d4ea8a 100644 --- a/src/gb/gprs_bssgp.c +++ b/src/gb/gprs_bssgp.c @@ -284,9 +284,8 @@ static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp, /* 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 6131e6b1..3af6517f 100644 --- a/src/gb/gprs_bssgp_vty.c +++ b/src/gb/gprs_bssgp_vty.c @@ -81,11 +81,9 @@ DEFUN(cfg_bssgp, cfg_bssgp_cmd, 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 f85aba39..b0d6dbd2 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 c0be374d..b43e0e63 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 @@ -50,6 +51,27 @@ struct msgb *gsm0808_create_layer3_aoip(const struct msgb *msg_l3, uint16_t nc, 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 { uint8_t ident; @@ -67,8 +89,8 @@ struct msgb *gsm0808_create_layer3_aoip(const struct msgb *msg_l3, uint16_t nc, /* 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); + gsm48_generate_lai2(&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 @@ struct msgb *gsm0808_create_layer3_aoip(const struct msgb *msg_l3, uint16_t nc, 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/gsm23003.c b/src/gsm/gsm23003.c index 95ac9f83..63de2b81 100644 --- a/src/gsm/gsm23003.c +++ b/src/gsm/gsm23003.c @@ -24,6 +24,7 @@ */ #include <ctype.h> +#include <stdio.h> #include <osmocom/gsm/gsm23003.h> #include <osmocom/gsm/protocol/gsm_23_003.h> @@ -66,3 +67,131 @@ bool osmo_msisdn_str_valid(const char *msisdn) { return is_n_digits(msisdn, 1, 15); } + +/*! 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 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; +} + +static void to_bcd(uint8_t *bcd, uint16_t val) +{ + bcd[2] = val % 10; + val = val / 10; + bcd[1] = val % 10; + val = val / 10; + bcd[0] = val % 10; +} + +/* 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. 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, plmn->mcc); + bcd_dst[0] = bcd[0] | (bcd[1] << 4); + bcd_dst[1] = bcd[2]; + + 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 { + bcd_dst[1] |= 0xf << 4; + bcd_dst[2] = bcd[1] | (bcd[2] << 4); + } +} + +/* 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 osmo_plmn_from_bcd(const uint8_t *bcd_src, struct osmo_plmn_id *plmn) +{ + plmn->mcc = (bcd_src[0] & 0x0f) * 100 + + (bcd_src[0] >> 4) * 10 + + (bcd_src[1] & 0x0f); + + if ((bcd_src[1] & 0xf0) == 0xf0) { + plmn->mnc = (bcd_src[2] & 0x0f) * 10 + + (bcd_src[2] >> 4); + plmn->mnc_3_digits = false; + } else { + plmn->mnc = (bcd_src[2] & 0x0f) * 100 + + (bcd_src[2] >> 4) * 10 + + (bcd_src[1] >> 4); + plmn->mnc_3_digits = true; + } +} diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c index b58e9e2c..c2affaeb 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> @@ -180,6 +181,19 @@ const char *rr_cause_name(uint8_t cause) return get_value_string(rr_cause_names, cause); } +/*! 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; +} + /* FIXME: convert to value_string */ static const char *cc_state_names[32] = { "NULL", @@ -418,15 +432,6 @@ const char *gsm48_mi_type_name(uint8_t mi) return get_value_string(mi_type_names, mi); } -static void to_bcd(uint8_t *bcd, uint16_t val) -{ - bcd[2] = val % 10; - val = val / 10; - bcd[1] = val % 10; - val = val / 10; - bcd[0] = val % 10; -} - /*! Checks is particular message is cipherable in A/Gb mode according to * 3GPP TS 24.008 § 4.7.1.2 * \param[in] hdr Message header @@ -451,64 +456,61 @@ bool gsm48_hdr_gmm_cipherable(const struct gsm48_hdr *hdr) } } -/* 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 - * - * Convert given mcc and mnc to BCD and write to *bcd_dst, which must be an - * allocated buffer of (at least) 3 bytes length. */ +/* 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) { - uint8_t bcd[3]; - - to_bcd(bcd, 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) { - bcd_dst[1] |= bcd[2] << 4; - bcd_dst[2] = bcd[0] | (bcd[1] << 4); - } else { - bcd_dst[1] |= 0xf << 4; - bcd_dst[2] = bcd[1] | (bcd[2] << 4); - } + 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 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 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) { - *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); - } else { - *mnc = (bcd_src[2] & 0x0f) * 100 - + (bcd_src[2] >> 4) * 10 - + (bcd_src[1] >> 4); - } + struct osmo_plmn_id plmn; + osmo_plmn_from_bcd(bcd_src, &plmn); + *mcc = plmn.mcc; + *mnc = plmn.mnc; } -/*! 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 gsm48_generate_lai2(), 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, + }; + gsm48_generate_lai2(lai48, &lai); +} + +/*! Encode TS 04.08 Location Area Identifier. + * \param[out] lai48 caller-provided memory for output. + * \param[in] lai input of MCC-MNC-LAC. */ +void gsm48_generate_lai2(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); } -/*! Decode TS 04.08 Location Area Identifier +/*! 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,11 +521,25 @@ void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc, 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; + gsm48_decode_lai2(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 gsm48_decode_lai2(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) * \param[in] op Cell Options structure in which DTX parameters will be set * \param[in] full Mode for full-rate channels @@ -682,10 +698,12 @@ void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf) 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 +722,7 @@ void gsm48_encode_ra(struct gsm48_ra_id *out, const struct gprs_ra_id *raid) 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 7a74718c..531c5c13 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -154,6 +154,7 @@ gsm0808_create_paging; 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_dtx_mode; gsm48_mi_type_name; gsm48_mcc_mnc_to_bcd; gsm48_mcc_mnc_from_bcd; +gsm48_generate_lai2; +gsm48_decode_lai2; +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 f66e3913..8c819a4f 100644 --- a/src/sim/card_fs_sim.c +++ b/src/sim/card_fs_sim.c @@ -216,6 +216,7 @@ static int gsm_plmnsel_decode(struct osim_decoded_data *dd, 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; |