diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h index 8323c724d..00fb6f405 100644 --- a/include/osmocom/gsm/gsm48.h +++ b/include/osmocom/gsm/gsm48.h @@ -20,7 +20,9 @@ * To mark an invalid / unset MNC, this value shall be used. */ #define GSM_MCC_MNC_INVALID 0xFFFF -/* A parsed GPRS routing area */ +/* A parsed GPRS routing area. + * Preferably use struct osmo_routing_area_id, it is better integrated with API like osmo_plmn_cmp(). + */ struct gprs_ra_id { uint16_t mcc; uint16_t mnc; @@ -104,6 +106,9 @@ int osmo_mobile_identity_encode_buf(uint8_t *buf, size_t buflen, const struct os int osmo_mobile_identity_encode_msgb(struct msgb *msg, const struct osmo_mobile_identity *mi, bool allow_hex); /* Parse Routeing Area Identifier */ +int osmo_routing_area_id_decode(struct osmo_routing_area_id *dst, const uint8_t *ra_data, size_t ra_data_len); +int osmo_routing_area_id_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_routing_area_id *src); +int osmo_routing_area_id_encode_msgb(struct msgb *msg, const struct osmo_routing_area_id *src); void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf); void gsm48_encode_ra(struct gsm48_ra_id *out, const struct gprs_ra_id *raid); int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid) OSMO_DEPRECATED("Use gsm48_encode_ra() instead"); diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c index df455acb3..53e0989c5 100644 --- a/src/gsm/gsm48.c +++ b/src/gsm/gsm48.c @@ -1334,7 +1334,64 @@ int gsm48_mi_to_string(char *string, int str_len, const uint8_t *mi, int mi_len) return 1; } -/*! Parse TS 04.08 Routing Area Identifier +/*! Decode to struct osmo_routing_area_id from a 3GPP TS 24.008 § 10.5.5.15 Routing area identification. + * \param[out] dst Store the decoded result here. + * \param[in] ra_data The start of a Routing Area ID in encoded form, to be decoded. + * \param[in] ra_data_len Buffer size available to read from at *ra_data. + * \return the number of decoded bytes on success, or negative on error (if the input buffer is too small). + */ +int osmo_routing_area_id_decode(struct osmo_routing_area_id *dst, const uint8_t *ra_data, size_t ra_data_len) +{ + const struct gsm48_ra_id *ra_id; + if (ra_data_len < sizeof(*ra_id)) + return -ENOSPC; + + gsm48_decode_lai2((void *)ra_data, &dst->lac); + + ra_id = (void *)ra_data; + dst->rac = ra_id->rac; + + return sizeof(*ra_id); +} + +/*! Encode struct osmo_routing_area_id to a 3GPP TS 24.008 § 10.5.5.15 Routing area identification: write to a buffer. + * \param[out] buf Return buffer for encoded Mobile Identity. + * \param[in] buflen sizeof(buf). + * \param[in] src RA to encode. + * \return Amount of bytes written to buf, or negative on error. + */ +int osmo_routing_area_id_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_routing_area_id *src) +{ + struct gsm48_ra_id *ra_id; + if (buflen < sizeof(*ra_id)) + return -ENOSPC; + + gsm48_generate_lai2((void *)buf, &src->lac); + + ra_id = (void *)buf; + ra_id->rac = src->rac; + + return sizeof(*ra_id); +} + +/*! Encode struct osmo_routing_area_id to a 3GPP TS 24.008 § 10.5.5.15 Routing area identification: append to msgb. + * To succeed, the msgb must have tailroom >= sizeof(struct gsm48_ra_id). + * \param[out] msg Append to this msgb. + * \param[in] src Encode this Routing Area ID. + * \return Number of bytes appended to msgb, or negative on error. + */ +int osmo_routing_area_id_encode_msgb(struct msgb *msg, const struct osmo_routing_area_id *src) +{ + int rc = osmo_routing_area_id_encode_buf(msg->tail, msgb_tailroom(msg), src); + if (rc <= 0) + return rc; + msgb_put(msg, rc); + return rc; +} + +/*! Parse TS 04.08 Routing Area Identifier. + * Preferably use osmo_routing_area_id_decode() instead: struct osmo_routing_area_id is better integrated with other API + * like osmo_plmn_cmp(). * \param[out] Caller-provided memory for decoded RA ID * \param[in] buf Input buffer pointing to RAI IE value */ void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf) diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index 0e45dc154..1eea819ee 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -415,6 +415,9 @@ osmo_mobile_identity_decode_from_l3; osmo_mobile_identity_encoded_len; osmo_mobile_identity_encode_buf; osmo_mobile_identity_encode_msgb; +osmo_routing_area_id_decode; +osmo_routing_area_id_encode_buf; +osmo_routing_area_id_encode_msgb; gsm48_mm_att_tlvdef; gsm48_number_of_paging_subchannels; gsm48_parse_ra; diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c index 4436d5175..f4353e258 100644 --- a/tests/gsm0408/gsm0408_test.c +++ b/tests/gsm0408/gsm0408_test.c @@ -341,6 +341,171 @@ static void test_lai_encode_decode(void) } } +static struct osmo_routing_area_id test_osmo_routing_area_id_items[] = { + { + .lac = { + .plmn = { + .mcc = 77, + .mnc = 121, + }, + .lac = 666, + }, + .rac = 5, + }, + { + .lac = { + .plmn = { + .mcc = 84, + .mnc = 98, + }, + .lac = 11, + }, + .rac = 89, + }, + { + .lac = { + .plmn = { + .mcc = 0, + .mnc = 0, + .mnc_3_digits = false, + /* expecting 000-00, BCD = 00 f0 00 */ + }, + .lac = 0, + }, + .rac = 0, + }, + { + .lac = { + .plmn = { + .mcc = 0, + .mnc = 0, + .mnc_3_digits = true, + /* expecting 000-000, BCD = 00 00 00 */ + }, + .lac = 0, + }, + .rac = 0, + }, + { + .lac = { + .plmn = { + .mcc = 999, + .mnc = 999, + }, + .lac = 65535, + }, + .rac = 255, + }, + { + .lac = { + .plmn = { + .mcc = 1, + .mnc = 2, + .mnc_3_digits = false, + /* expecting 001-02, BCD = 00 f1 20 */ + }, + .lac = 23, + }, + .rac = 42, + }, + { + .lac = { + .plmn = { + .mcc = 1, + .mnc = 2, + .mnc_3_digits = true, + /* expecting 001-002, BCD = 00 21 00 */ + }, + .lac = 23, + }, + .rac = 42, + }, + { + .lac = { + .plmn = { + .mcc = 12, + .mnc = 34, + .mnc_3_digits = false, + /* expecting 012-34, BCD = 10 f2 43 */ + }, + .lac = 56, + }, + .rac = 78, + }, + { + .lac = { + .plmn = { + .mcc = 12, + .mnc = 34, + .mnc_3_digits = true, + /* expecting 012-034, BCD = 10 42 30 */ + }, + .lac = 23, + }, + .rac = 42, + }, + { + .lac = { + .plmn = { + .mcc = 123, + .mnc = 456, + .mnc_3_digits = false, + /* expecting 123-456, BCD = 21 63 54 (false flag has no effect) */ + }, + .lac = 23, + }, + .rac = 42, + }, + { + .lac = { + .plmn = { + .mcc = 123, + .mnc = 456, + .mnc_3_digits = true, + /* expecting 123-456, BCD = 21 63 54 (same) */ + }, + .lac = 23, + }, + .rac = 42, + }, +}; + +static inline void dump_osmo_routing_area_id(const struct osmo_routing_area_id *raid) +{ + printf("%s%s", osmo_rai_name2(raid), raid->lac.plmn.mnc_3_digits ? " (3-digit MNC)" : ""); +} + +static inline void check_osmo_routing_area_id(const struct osmo_routing_area_id *raid) +{ + uint8_t buf[sizeof(struct gsm48_ra_id)] = {}; + struct osmo_routing_area_id raid0 = {}; + int rc; + + printf("RA ID: "); + dump_osmo_routing_area_id(raid); + + rc = osmo_routing_area_id_encode_buf(buf, sizeof(buf), raid); + printf("osmo_routing_area_id_encode_buf(): %src=%d\n", osmo_hexdump(buf, sizeof(buf)), rc); + + rc = osmo_routing_area_id_decode(&raid0, buf, sizeof(buf)); + printf("osmo_routing_area_id_decode(): "); + dump_osmo_routing_area_id(&raid0); + printf(" rc=%d\n", rc); + + if (osmo_rai_cmp(raid, &raid0)) + printf("FAIL\n"); + else + printf("ok\n"); +} + +static void test_osmo_routing_area_id(void) +{ + int i; + printf("==%s()==\n", __func__); + for (i = 0; i < ARRAY_SIZE(test_osmo_routing_area_id_items); i++) + check_osmo_routing_area_id(&test_osmo_routing_area_id_items[i]); +} + static void dump_cm3(struct gsm48_classmark3 *cm3) { printf("mult_band_supp=%02x\n", cm3->mult_band_supp); @@ -1792,6 +1957,7 @@ int main(int argc, char **argv) test_bcd_number_encode_decode(); test_ra_cap(); test_lai_encode_decode(); + test_osmo_routing_area_id(); test_decode_classmark3(); test_si_range_helpers(); diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok index dc48f84a8..b966865d7 100644 --- a/tests/gsm0408/gsm0408_test.ok +++ b/tests/gsm0408/gsm0408_test.ok @@ -386,6 +386,40 @@ RA test...passed Encoded 21 63 54 00 17 gsm48_decode_lai2() gives 123-456-23 (3-digit MNC) passed +==test_osmo_routing_area_id()== +RA ID: 077-121-666-5osmo_routing_area_id_encode_buf(): 70 17 21 02 9a 05 rc=6 +osmo_routing_area_id_decode(): 077-121-666-5 (3-digit MNC) rc=6 +ok +RA ID: 084-98-11-89osmo_routing_area_id_encode_buf(): 80 f4 89 00 0b 59 rc=6 +osmo_routing_area_id_decode(): 084-98-11-89 rc=6 +ok +RA ID: 000-00-0-0osmo_routing_area_id_encode_buf(): 00 f0 00 00 00 00 rc=6 +osmo_routing_area_id_decode(): 000-00-0-0 rc=6 +ok +RA ID: 000-000-0-0 (3-digit MNC)osmo_routing_area_id_encode_buf(): 00 00 00 00 00 00 rc=6 +osmo_routing_area_id_decode(): 000-000-0-0 (3-digit MNC) rc=6 +ok +RA ID: 999-999-65535-255osmo_routing_area_id_encode_buf(): 99 99 99 ff ff ff rc=6 +osmo_routing_area_id_decode(): 999-999-65535-255 (3-digit MNC) rc=6 +ok +RA ID: 001-02-23-42osmo_routing_area_id_encode_buf(): 00 f1 20 00 17 2a rc=6 +osmo_routing_area_id_decode(): 001-02-23-42 rc=6 +ok +RA ID: 001-002-23-42 (3-digit MNC)osmo_routing_area_id_encode_buf(): 00 21 00 00 17 2a rc=6 +osmo_routing_area_id_decode(): 001-002-23-42 (3-digit MNC) rc=6 +ok +RA ID: 012-34-56-78osmo_routing_area_id_encode_buf(): 10 f2 43 00 38 4e rc=6 +osmo_routing_area_id_decode(): 012-34-56-78 rc=6 +ok +RA ID: 012-034-23-42 (3-digit MNC)osmo_routing_area_id_encode_buf(): 10 42 30 00 17 2a rc=6 +osmo_routing_area_id_decode(): 012-034-23-42 (3-digit MNC) rc=6 +ok +RA ID: 123-456-23-42osmo_routing_area_id_encode_buf(): 21 63 54 00 17 2a rc=6 +osmo_routing_area_id_decode(): 123-456-23-42 (3-digit MNC) rc=6 +ok +RA ID: 123-456-23-42 (3-digit MNC)osmo_routing_area_id_encode_buf(): 21 63 54 00 17 2a rc=6 +osmo_routing_area_id_decode(): 123-456-23-42 (3-digit MNC) rc=6 +ok =====cm3_1===== mult_band_supp=06 a5_bits=00