diff options
Diffstat (limited to 'src/gsm/gsm48.c')
-rw-r--r-- | src/gsm/gsm48.c | 265 |
1 files changed, 228 insertions, 37 deletions
diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c index eb1d055b..64b17658 100644 --- a/src/gsm/gsm48.c +++ b/src/gsm/gsm48.c @@ -19,10 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -46,6 +42,7 @@ #include <osmocom/gsm/protocol/gsm_08_58.h> #include <osmocom/gsm/protocol/gsm_04_08_gprs.h> #include <osmocom/gsm/protocol/gsm_23_003.h> +#include <osmocom/gsm/protocol/gsm_44_068.h> /*! \addtogroup gsm0408 * @{ @@ -124,6 +121,7 @@ const struct tlv_definition gsm48_rr_att_tlvdef = { [GSM48_IE_CHDES_2_AFTER] = { TLV_TYPE_FIXED, 3 }, [GSM48_IE_MODE_SEC_CH] = { TLV_TYPE_TV }, [GSM48_IE_F_CH_SEQ_AFTER] = { TLV_TYPE_FIXED, 9 }, + [GSM48_IE_EXTENDED_TSC_SET] = { TLV_TYPE_TV }, [GSM48_IE_MA_AFTER] = { TLV_TYPE_TLV }, [GSM48_IE_BA_RANGE] = { TLV_TYPE_TLV }, [GSM48_IE_GROUP_CHDES] = { TLV_TYPE_TLV }, @@ -132,10 +130,10 @@ const struct tlv_definition gsm48_rr_att_tlvdef = { [GSM48_IE_REALTIME_DIFF] = { TLV_TYPE_TLV }, [GSM48_IE_START_TIME] = { TLV_TYPE_FIXED, 2 }, [GSM48_IE_TIMING_ADVANCE] = { TLV_TYPE_TV }, - [GSM48_IE_GROUP_CIP_SEQ] = { TLV_TYPE_SINGLE_TV }, - [GSM48_IE_CIP_MODE_SET] = { TLV_TYPE_SINGLE_TV }, - [GSM48_IE_GPRS_RESUMPT] = { TLV_TYPE_SINGLE_TV }, - [GSM48_IE_SYNC_IND] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_GROUP_CIP_SEQ_HO] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_CIP_MODE_SET_HO] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_GPRS_RESUMPT_HO] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_SYNC_IND_HO] = { TLV_TYPE_SINGLE_TV }, }, }; @@ -151,7 +149,7 @@ const struct tlv_definition gsm48_mm_att_tlvdef = { [GSM48_IE_NET_DST] = { TLV_TYPE_TLV }, [GSM48_IE_LOCATION_AREA] = { TLV_TYPE_FIXED, 5 }, - [GSM48_IE_PRIORITY_LEV] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_PRIORITY_LEV_HO] = { TLV_TYPE_SINGLE_TV }, [GSM48_IE_FOLLOW_ON_PROC] = { TLV_TYPE_T }, [GSM48_IE_CTS_PERMISSION] = { TLV_TYPE_T }, }, @@ -164,13 +162,16 @@ static const struct value_string rr_cause_names[] = { { GSM48_RR_CAUSE_ABNORMAL_TIMER, "Abnormal release, timer expired" }, { GSM48_RR_CAUSE_ABNORMAL_NOACT, "Abnormal release, no activity on radio path" }, { GSM48_RR_CAUSE_PREMPTIVE_REL, "Preemptive release" }, + { GSM48_RR_CAUSE_UTRAN_CFG_UNK, "UTRAN configuration unknown" }, { GSM48_RR_CAUSE_HNDOVER_IMP, "Handover impossible, timing advance out of range" }, { GSM48_RR_CAUSE_CHAN_MODE_UNACCT, "Channel mode unacceptable" }, { GSM48_RR_CAUSE_FREQ_NOT_IMPL, "Frequency not implemented" }, + { GSM48_RR_CAUSE_LEAVE_GROUP_CA, "Originator or talker leaving group call area" }, + { GSM48_RR_CAUSE_LOW_LEVEL_FAIL, "Lower layer failure" }, { GSM48_RR_CAUSE_CALL_CLEARED, "Call already cleared" }, { GSM48_RR_CAUSE_SEMANT_INCORR, "Semantically incorrect message" }, { GSM48_RR_CAUSE_INVALID_MAND_INF, "Invalid mandatory information" }, - { GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existant or not implemented" }, + { GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existent or not implemented" }, { GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT, "Message type not compatible with protocol state" }, { GSM48_RR_CAUSE_COND_IE_ERROR, "Conditional IE error" }, { GSM48_RR_CAUSE_NO_CELL_ALLOC_A, "No cell allocation available" }, @@ -419,19 +420,102 @@ const char *gsm48_rr_msg_name(uint8_t msgtype) return get_value_string(rr_msg_names, msgtype); } +/* 3GPP TS 44.018 Table 10.4.2 */ +static const struct value_string rr_msg_type_short_names[] = { + { GSM48_MT_RR_SH_SI10, "System Information Type 10" }, + { GSM48_MT_RR_SH_FACCH, "Notification/FACCH" }, + { GSM48_MT_RR_SH_UL_FREE, "Uplink Free" }, + { GSM48_MT_RR_SH_MEAS_REP, "Enhanced Measurement Report (uplink)" }, + { GSM48_MT_RR_SH_MEAS_INFO, "Measurement Information (downlink)" }, + { GSM48_MT_RR_SH_VGCS_RECON, "VBS/VGCS Reconfigure" }, + { GSM48_MT_RR_SH_VGCS_RECON2, "VBS/VGCS Reconfigure2" }, + { GSM48_MT_RR_SH_VGCS_INFO, "VGCS Additional Information" }, + { GSM48_MT_RR_SH_VGCS_SMS, "VGCS SMS Information" }, + { GSM48_MT_RR_SH_SI10bis, "System Information Type 10bis" }, + { GSM48_MT_RR_SH_SI10ter, "System Information Type 10ter" }, + { GSM48_MT_RR_SH_VGCS_NEIGH, "VGCS Neighbour Cell Information" }, + { GSM48_MT_RR_SH_APP_DATA, "Notify Application Data" }, + { 0, NULL } +}; + +/*! return string representation of RR Message Type using the RR short protocol discriminator */ +const char *gsm48_rr_short_pd_msg_name(uint8_t msgtype) +{ + return get_value_string(rr_msg_type_short_names, msgtype); +} const struct value_string gsm48_chan_mode_names[] = { { GSM48_CMODE_SIGN, "SIGNALLING" }, { GSM48_CMODE_SPEECH_V1, "SPEECH_V1" }, { GSM48_CMODE_SPEECH_EFR, "SPEECH_EFR" }, { GSM48_CMODE_SPEECH_AMR, "SPEECH_AMR" }, + { GSM48_CMODE_SPEECH_V4, "SPEECH_V4" }, + { GSM48_CMODE_SPEECH_V5, "SPEECH_V5" }, + { GSM48_CMODE_SPEECH_V6, "SPEECH_V6" }, + + { GSM48_CMODE_DATA_43k5_14k5, "DATA_43k5_14k5" }, + { GSM48_CMODE_DATA_29k0_14k5, "DATA_29k0_14k5" }, + { GSM48_CMODE_DATA_43k5_29k0, "DATA_43k5_29k0" }, + { GSM48_CMODE_DATA_14k5_43k5, "DATA_14k5_43k5" }, + { GSM48_CMODE_DATA_14k5_29k0, "DATA_14k5_29k0" }, + { GSM48_CMODE_DATA_29k0_43k5, "DATA_29k0_43k5" }, + + { GSM48_CMODE_DATA_43k5, "DATA_43k5" }, + { GSM48_CMODE_DATA_32k0, "DATA_32k0" }, + { GSM48_CMODE_DATA_29k0, "DATA_29k0" }, { GSM48_CMODE_DATA_14k5, "DATA_14k5" }, { GSM48_CMODE_DATA_12k0, "DATA_12k0" }, { GSM48_CMODE_DATA_6k0, "DATA_6k0" }, { GSM48_CMODE_DATA_3k6, "DATA_3k6" }, + + { GSM48_CMODE_SPEECH_V1_VAMOS, "SPEECH_V1_VAMOS" }, + { GSM48_CMODE_SPEECH_V2_VAMOS, "SPEECH_V2_VAMOS" }, + { GSM48_CMODE_SPEECH_V3_VAMOS, "SPEECH_V3_VAMOS" }, + { GSM48_CMODE_SPEECH_V5_VAMOS, "SPEECH_V5_VAMOS" }, { 0, NULL }, }; +/*! Translate GSM48_CMODE_SPEECH_* to its corresponding GSM48_CMODE_SPEECH_*_VAMOS mode. + * If the mode has no equivalent VAMOS mode, return a negative value. + */ +enum gsm48_chan_mode gsm48_chan_mode_to_vamos(enum gsm48_chan_mode mode) +{ + switch (mode) { + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_V1_VAMOS: + return GSM48_CMODE_SPEECH_V1_VAMOS; + case GSM48_CMODE_SPEECH_EFR: + case GSM48_CMODE_SPEECH_V2_VAMOS: + return GSM48_CMODE_SPEECH_V2_VAMOS; + case GSM48_CMODE_SPEECH_AMR: + case GSM48_CMODE_SPEECH_V3_VAMOS: + return GSM48_CMODE_SPEECH_V3_VAMOS; + case GSM48_CMODE_SPEECH_V5_VAMOS: + return GSM48_CMODE_SPEECH_V5_VAMOS; + default: + return -1; + } +} + +/*! Translate GSM48_CMODE_SPEECH_*_VAMOS to its corresponding GSM48_CMODE_SPEECH_* non-vamos mode. + * If the mode is not a VAMOS mode, return the unchanged mode. + */ +enum gsm48_chan_mode gsm48_chan_mode_to_non_vamos(enum gsm48_chan_mode mode) +{ + switch (mode) { + case GSM48_CMODE_SPEECH_V1_VAMOS: + return GSM48_CMODE_SPEECH_V1; + case GSM48_CMODE_SPEECH_V2_VAMOS: + return GSM48_CMODE_SPEECH_EFR; + case GSM48_CMODE_SPEECH_V3_VAMOS: + return GSM48_CMODE_SPEECH_AMR; + case GSM48_CMODE_SPEECH_V5_VAMOS: + return GSM48_CMODE_SPEECH_V5; + default: + return mode; + } +} + const struct value_string gsm_chan_t_names[] = { { GSM_LCHAN_NONE, "NONE" }, { GSM_LCHAN_SDCCH, "SDCCH" }, @@ -554,11 +638,13 @@ int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t * { int rc; int nibbles_len; - char *str; - size_t str_size; + char *str = NULL; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */ + size_t str_size = 0; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */ - if (!mi_data || mi_len < 1) - return -EBADMSG; + if (!mi_data || mi_len < 1) { + rc = -EBADMSG; + goto return_error; + } nibbles_len = (mi_len - 1) * 2 + ((mi_data[0] & GSM_MI_ODD) ? 1 : 0); @@ -630,8 +716,12 @@ int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t * goto return_error; } rc = osmo_bcd2str(str, str_size, mi_data, 1, 1 + nibbles_len, allow_hex); - /* rc checked below */ - break; + /* check mi->str printing rc */ + if (rc < 1 || rc >= str_size) { + rc = -EBADMSG; + goto return_error; + } + return 0; default: /* Already handled above, but as future bug paranoia: */ @@ -639,13 +729,6 @@ int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t * goto return_error; } - /* check mi->str printing rc */ - if (rc < 1 || rc >= str_size) { - rc = -EBADMSG; - goto return_error; - } - return 0; - return_error: *mi = (struct osmo_mobile_identity){ .type = GSM_MI_TYPE_NONE, @@ -783,11 +866,13 @@ int osmo_mobile_identity_encode_msgb(struct msgb *msg, const struct osmo_mobile_ * osmo_mobile_identity. * * \param[out] mi Return buffer for decoded Mobile Identity. - * \param[in] msg The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp). + * \param[in] l3_data The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp). + * \param[in] l3_len Length of l3_data in bytes. * \returns 0 on success, negative on error: return codes as defined in osmo_mobile_identity_decode(), or * -ENOTSUP = not a Complete Layer 3 message, */ -int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct msgb *msg, bool allow_hex) +int osmo_mobile_identity_decode_from_l3_buf(struct osmo_mobile_identity *mi, const uint8_t *l3_data, size_t l3_len, + bool allow_hex) { const struct gsm48_hdr *gh; int8_t pdisc = 0; @@ -806,10 +891,10 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct .tmsi = GSM_RESERVED_TMSI, }; - if (msgb_l3len(msg) < sizeof(*gh)) + if (l3_len < sizeof(*gh)) return -EBADMSG; - gh = msgb_l3(msg); + gh = (void *)l3_data; pdisc = gsm48_hdr_pdisc(gh); mtype = gsm48_hdr_msg_type(gh); @@ -819,12 +904,12 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct switch (mtype) { case GSM48_MT_MM_LOC_UPD_REQUEST: /* First make sure that lu-> can be dereferenced */ - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) + if (l3_len < sizeof(*gh) + sizeof(*lu)) return -EBADMSG; /* Now we know there is enough msgb data to read a lu->mi_len, so also check that */ lu = (struct gsm48_loc_upd_req*)gh->data; - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu) + lu->mi_len) + if (l3_len < sizeof(*gh) + sizeof(*lu) + lu->mi_len) return -EBADMSG; mi_data = lu->mi; mi_len = lu->mi_len; @@ -834,7 +919,7 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct case GSM48_MT_MM_CM_REEST_REQ: /* Unfortunately in Phase1 the Classmark2 length is variable, so we cannot * just use gsm48_service_request struct, and need to parse it manually. */ - if (msgb_l3len(msg) < sizeof(*gh) + 2) + if (l3_len < sizeof(*gh) + 2) return -EBADMSG; cm2_len = gh->data[1]; @@ -842,7 +927,7 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct goto got_cm2; case GSM48_MT_MM_IMSI_DETACH_IND: - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*idi)) + if (l3_len < sizeof(*gh) + sizeof(*idi)) return -EBADMSG; idi = (struct gsm48_imsi_detach_ind*) gh->data; mi_data = idi->mi; @@ -850,7 +935,7 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct goto got_mi; case GSM48_MT_MM_ID_RESP: - if (msgb_l3len(msg) < sizeof(*gh) + 2) + if (l3_len < sizeof(*gh) + 2) return -EBADMSG; mi_data = gh->data+1; mi_len = gh->data[0]; @@ -865,13 +950,24 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct switch (mtype) { case GSM48_MT_RR_PAG_RESP: - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*paging_response)) + if (l3_len < sizeof(*gh) + sizeof(*paging_response)) return -EBADMSG; paging_response = (struct gsm48_pag_resp*)gh->data; cm2_len = paging_response->cm2_len; cm2_buf = (uint8_t*)&paging_response->cm2; goto got_cm2; + case GSM48_MT_RR_TALKER_IND: + /* Check minimum size: Header + CM2 LV + minimum MI LV */ + if (l3_len < sizeof(*gh) + 4 + 2) + return -EBADMSG; + /* CM2 shall be always 3 bytes in length */ + if (gh->data[0] != 3) + return -EBADMSG; + cm2_len = gh->data[0]; + cm2_buf = gh->data + 1; + goto got_cm2; + default: break; } @@ -884,7 +980,7 @@ got_cm2: /* MI (Mobile Identity) LV follows the Classmark2 */ /* There must be at least a mi_len byte after the CM2 */ - if (cm2_buf + cm2_len + 1 > msg->tail) + if (cm2_buf + cm2_len + 1 > l3_data + l3_len) return -EBADMSG; mi_start = cm2_buf + cm2_len; @@ -893,12 +989,27 @@ got_cm2: got_mi: /* mi_data points at the start of the Mobile Identity coding of mi_len bytes */ - if (mi_data + mi_len > msg->tail) + if (mi_data + mi_len > l3_data + l3_len) return -EBADMSG; return osmo_mobile_identity_decode(mi, mi_data, mi_len, allow_hex); } +/*! Extract Mobile Identity from a Complete Layer 3 message. + * + * Determine the Mobile Identity data and call osmo_mobile_identity_decode() to return a decoded struct + * osmo_mobile_identity. + * + * \param[out] mi Return buffer for decoded Mobile Identity. + * \param[in] msg The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp). + * \returns 0 on success, negative on error: return codes as defined in osmo_mobile_identity_decode(), or + * -ENOTSUP = not a Complete Layer 3 message, + */ +int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct msgb *msg, bool allow_hex) +{ + return osmo_mobile_identity_decode_from_l3_buf(mi, msgb_l3(msg), msgb_l3len(msg), allow_hex); +} + /*! Return a human readable representation of a struct osmo_mobile_identity. * Write a string like "IMSI-1234567", "TMSI-0x1234ABCD" or "NONE", "NULL". * \param[out] buf String buffer to write to. @@ -1237,7 +1348,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) @@ -1295,6 +1463,25 @@ int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid) return 6; } +/*! Compare a TS 04.08 Routing Area Identifier + * \param[in] raid1 first Routing Area ID to compare. + * \param[in] raid2 second Routing Area ID to compare. + * \returns true if raid1 and raid2 match, false otherwise. */ +bool gsm48_ra_equal(const struct gprs_ra_id *raid1, const struct gprs_ra_id *raid2) +{ + if (raid1->mcc != raid2->mcc) + return false; + if (raid1->mnc != raid2->mnc) + return false; + if (raid1->mnc_3_digits != raid2->mnc_3_digits) + return false; + if (raid1->lac != raid2->lac) + return false; + if (raid1->rac != raid2->rac) + return false; + return true; +} + /*! Determine number of paging sub-channels * \param[in] chan_desc Control Channel Description * \returns number of paging sub-channels @@ -1302,7 +1489,7 @@ int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid) * Uses From Table 10.5.33 of GSM 04.08 to determine the number of * paging sub-channels in the given control channel configuration */ -int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc) +int gsm48_number_of_paging_subchannels(const struct gsm48_control_channel_descr *chan_desc) { unsigned int n_pag_blocks = gsm0502_get_n_pag_blocks(chan_desc); @@ -1582,6 +1769,10 @@ char *gsm48_pdisc_msgtype_name_buf(char *buf, size_t buf_len, uint8_t pdisc, uin case GSM48_PDISC_CC: msgt_names = gsm48_cc_msgtype_names; break; + case GSM48_PDISC_GROUP_CC: + case GSM48_PDISC_BCAST_CC: + msgt_names = osmo_gsm44068_msg_type_names; + break; case GSM48_PDISC_NC_SS: msgt_names = gsm48_nc_ss_msgtype_names; break; |