blob: 63de2b81afbad10c5b87eab48a9890ebe29578ab [file] [log] [blame]
Neels Hofmeyr9cd1e742017-10-04 03:15:47 +02001/*! \file gsm23003.c
2 * Utility function implementations related to 3GPP TS 23.003 */
3/*
Harald Weltee08da972017-11-13 01:00:26 +09004 * (C) 2017 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
5 * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
Neels Hofmeyr9cd1e742017-10-04 03:15:47 +02006 * All Rights Reserved
7 *
Harald Weltee08da972017-11-13 01:00:26 +09008 * SPDX-License-Identifier: GPL-2.0+
Neels Hofmeyr9cd1e742017-10-04 03:15:47 +02009 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 *
24 */
25
26#include <ctype.h>
Neels Hofmeyrc4fce142018-02-20 13:47:08 +010027#include <stdio.h>
Neels Hofmeyr9cd1e742017-10-04 03:15:47 +020028
29#include <osmocom/gsm/gsm23003.h>
30#include <osmocom/gsm/protocol/gsm_23_003.h>
31
32static bool is_n_digits(const char *str, int min_digits, int max_digits)
33{
34 int len;
35 /* Use unsigned char * to avoid a compiler warning of
36 * "error: array subscript has type 'char' [-Werror=char-subscripts]" */
37 const unsigned char *pos = (const unsigned char *)str;
Neels Hofmeyr4b7c7912017-10-07 04:45:01 +020038 if (!pos)
39 return min_digits < 1;
Neels Hofmeyr9cd1e742017-10-04 03:15:47 +020040 for (len = 0; *pos && len < max_digits; len++, pos++)
41 if (!isdigit(*pos))
42 return false;
43 if (len < min_digits)
44 return false;
45 /* With not too many digits, we should have reached *str == nul */
46 if (*pos)
47 return false;
48 return true;
49}
50
51/*! Determine whether the given IMSI is valid according to 3GPP TS 23.003.
52 * \param imsi IMSI digits in ASCII string representation.
53 * \returns true when the IMSI is valid, false for invalid characters or number
54 * of digits.
55 */
56bool osmo_imsi_str_valid(const char *imsi)
57{
58 return is_n_digits(imsi, GSM23003_IMSI_MIN_DIGITS, GSM23003_IMSI_MAX_DIGITS);
59}
60
61/*! Determine whether the given MSISDN is valid according to 3GPP TS 23.003.
62 * \param msisdn MSISDN digits in ASCII string representation.
63 * \returns true when the MSISDN is valid, false for invalid characters or number
64 * of digits.
65 */
66bool osmo_msisdn_str_valid(const char *msisdn)
67{
68 return is_n_digits(msisdn, 1, 15);
69}
Neels Hofmeyrc4fce142018-02-20 13:47:08 +010070
71/*! Return MCC string as standardized 3-digit with leading zeros.
72 * \param[in] mcc MCC value.
73 * \returns string in static buffer.
74 */
75const char *osmo_mcc_name(uint16_t mcc)
76{
77 static char buf[8];
78 snprintf(buf, sizeof(buf), "%03u", mcc);
79 return buf;
80}
81
82/*! Return MNC string as standardized 2- or 3-digit with leading zeros.
83 * \param[in] mnc MNC value.
84 * \param[in] mnc_3_digits True if an MNC should fill three digits, only has an effect if MNC < 100.
85 * \returns string in static buffer.
86 */
87const char *osmo_mnc_name(uint16_t mnc, bool mnc_3_digits)
88{
89 static char buf[8];
90 snprintf(buf, sizeof(buf), "%0*u", mnc_3_digits ? 3 : 2, mnc);
91 return buf;
92}
93
94static inline void plmn_name(char *buf, size_t buflen, const struct osmo_plmn_id *plmn)
95{
96 snprintf(buf, buflen, "%s-%s", osmo_mcc_name(plmn->mcc),
97 osmo_mnc_name(plmn->mnc, plmn->mnc_3_digits));
98}
99
100/*! Return MCC-MNC string as standardized 3-digit-dash-2/3-digit with leading zeros.
101 * \param[in] plmn MCC-MNC value.
102 * \returns string in static buffer.
103 */
104const char *osmo_plmn_name(const struct osmo_plmn_id *plmn)
105{
106 static char buf[16];
107 plmn_name(buf, sizeof(buf), plmn);
108 return buf;
109}
110
111/*! Same as osmo_mcc_mnc_name(), but returning in a different static buffer.
112 * \param[in] plmn MCC-MNC value.
113 * \returns string in static buffer.
114 */
115const char *osmo_plmn_name2(const struct osmo_plmn_id *plmn)
116{
117 static char buf[16];
118 plmn_name(buf, sizeof(buf), plmn);
119 return buf;
120}
121
122/*! Return MCC-MNC-LAC as string, in a static buffer.
123 * \param[in] lai LAI to encode, the rac member is ignored.
124 * \returns Static string buffer.
125 */
126const char *osmo_lai_name(const struct osmo_location_area_id *lai)
127{
128 static char buf[32];
129 snprintf(buf, sizeof(buf), "%s-%u", osmo_plmn_name(&lai->plmn), lai->lac);
130 return buf;
131}
132
133static void to_bcd(uint8_t *bcd, uint16_t val)
134{
135 bcd[2] = val % 10;
136 val = val / 10;
137 bcd[1] = val % 10;
138 val = val / 10;
139 bcd[0] = val % 10;
140}
141
142/* Convert MCC + MNC to BCD representation
143 * \param[out] bcd_dst caller-allocated memory for output
144 * \param[in] mcc Mobile Country Code
145 * \param[in] mnc Mobile Network Code
146 * \param[in] mnc_3_digits true if the MNC shall have three digits.
147 *
148 * Convert given mcc and mnc to BCD and write to *bcd_dst, which must be an
149 * allocated buffer of (at least) 3 bytes length. Encode the MNC in three
150 * digits if its integer value is > 99, or if mnc_3_digits is passed true.
151 * Encode an MNC < 100 with mnc_3_digits passed as true as a three-digit MNC
152 * with leading zeros in the BCD representation.
153 */
154void osmo_plmn_to_bcd(uint8_t *bcd_dst, const struct osmo_plmn_id *plmn)
155{
156 uint8_t bcd[3];
157
158 to_bcd(bcd, plmn->mcc);
159 bcd_dst[0] = bcd[0] | (bcd[1] << 4);
160 bcd_dst[1] = bcd[2];
161
162 to_bcd(bcd, plmn->mnc);
163 if (plmn->mnc > 99 || plmn->mnc_3_digits) {
164 bcd_dst[1] |= bcd[2] << 4;
165 bcd_dst[2] = bcd[0] | (bcd[1] << 4);
166 } else {
167 bcd_dst[1] |= 0xf << 4;
168 bcd_dst[2] = bcd[1] | (bcd[2] << 4);
169 }
170}
171
172/* Convert given 3-byte BCD buffer to integers and write results to *mcc and
173 * *mnc. The first three BCD digits result in the MCC and the remaining ones in
174 * the MNC. Return mnc_3_digits as false if the MNC's most significant digit is encoded as 0xF, true
175 * otherwise; i.e. true if MNC > 99 or if it is represented with leading zeros instead of 0xF.
176 * \param[in] bcd_src 3-byte BCD buffer containing MCC+MNC representations.
177 * \param[out] mcc MCC result buffer, or NULL.
178 * \param[out] mnc MNC result buffer, or NULL.
179 * \param[out] mnc_3_digits Result buffer for 3-digit flag, or NULL.
180 */
181void osmo_plmn_from_bcd(const uint8_t *bcd_src, struct osmo_plmn_id *plmn)
182{
183 plmn->mcc = (bcd_src[0] & 0x0f) * 100
184 + (bcd_src[0] >> 4) * 10
185 + (bcd_src[1] & 0x0f);
186
187 if ((bcd_src[1] & 0xf0) == 0xf0) {
188 plmn->mnc = (bcd_src[2] & 0x0f) * 10
189 + (bcd_src[2] >> 4);
190 plmn->mnc_3_digits = false;
191 } else {
192 plmn->mnc = (bcd_src[2] & 0x0f) * 100
193 + (bcd_src[2] >> 4) * 10
194 + (bcd_src[1] >> 4);
195 plmn->mnc_3_digits = true;
196 }
197}