Harald Welte | 3e6376d | 2010-12-22 23:54:51 +0100 | [diff] [blame] | 1 | /* mncc.c - utility routines for the MNCC API between the 04.08 |
| 2 | * message parsing and the actual Call Control logic */ |
Harald Welte | 7ce5e25 | 2010-12-22 02:02:48 +0100 | [diff] [blame] | 3 | |
Harald Welte | 5718429 | 2018-01-22 01:49:02 +0100 | [diff] [blame] | 4 | /* (C) 2008-2018 by Harald Welte <laforge@gnumonks.org> |
Harald Welte | 4bfdfe7 | 2009-06-10 23:11:52 +0800 | [diff] [blame] | 5 | * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de> |
| 6 | * All Rights Reserved |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
Harald Welte | 9af6ddf | 2011-01-01 15:25:50 +0100 | [diff] [blame] | 9 | * it under the terms of the GNU Affero General Public License as published by |
| 10 | * the Free Software Foundation; either version 3 of the License, or |
Harald Welte | 4bfdfe7 | 2009-06-10 23:11:52 +0800 | [diff] [blame] | 11 | * (at your option) any later version. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
Harald Welte | 9af6ddf | 2011-01-01 15:25:50 +0100 | [diff] [blame] | 16 | * GNU Affero General Public License for more details. |
Harald Welte | 4bfdfe7 | 2009-06-10 23:11:52 +0800 | [diff] [blame] | 17 | * |
Harald Welte | 9af6ddf | 2011-01-01 15:25:50 +0100 | [diff] [blame] | 18 | * You should have received a copy of the GNU Affero General Public License |
| 19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
Harald Welte | 4bfdfe7 | 2009-06-10 23:11:52 +0800 | [diff] [blame] | 20 | * |
| 21 | */ |
| 22 | |
| 23 | |
| 24 | #include <stdio.h> |
| 25 | #include <stdlib.h> |
| 26 | #include <string.h> |
Harald Welte | 09b7e7f | 2009-12-12 21:36:53 +0100 | [diff] [blame] | 27 | #include <errno.h> |
Harald Welte | 4bfdfe7 | 2009-06-10 23:11:52 +0800 | [diff] [blame] | 28 | |
Harald Welte | da8a19f | 2015-12-03 14:35:05 +0100 | [diff] [blame] | 29 | #include <osmocom/core/talloc.h> |
| 30 | #include <osmocom/core/utils.h> |
| 31 | |
Neels Hofmeyr | 9084396 | 2017-09-04 15:04:35 +0200 | [diff] [blame] | 32 | #include <osmocom/msc/gsm_04_08.h> |
| 33 | #include <osmocom/msc/debug.h> |
| 34 | #include <osmocom/msc/mncc.h> |
| 35 | #include <osmocom/msc/gsm_data.h> |
| 36 | #include <osmocom/msc/transaction.h> |
Harald Welte | 2cf161b | 2009-06-20 22:36:41 +0200 | [diff] [blame] | 37 | |
Harald Welte | 4bfdfe7 | 2009-06-10 23:11:52 +0800 | [diff] [blame] | 38 | |
Harald Welte | da8a19f | 2015-12-03 14:35:05 +0100 | [diff] [blame] | 39 | static const struct value_string mncc_names[] = { |
| 40 | { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" }, |
| 41 | { MNCC_SETUP_IND, "MNCC_SETUP_IND" }, |
| 42 | { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" }, |
| 43 | { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" }, |
| 44 | { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" }, |
| 45 | { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" }, |
| 46 | { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" }, |
| 47 | { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" }, |
| 48 | { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" }, |
| 49 | { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" }, |
| 50 | { MNCC_ALERT_IND, "MNCC_ALERT_IND" }, |
| 51 | { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" }, |
| 52 | { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" }, |
| 53 | { MNCC_DISC_REQ, "MNCC_DISC_REQ" }, |
| 54 | { MNCC_DISC_IND, "MNCC_DISC_IND" }, |
| 55 | { MNCC_REL_REQ, "MNCC_REL_REQ" }, |
| 56 | { MNCC_REL_IND, "MNCC_REL_IND" }, |
| 57 | { MNCC_REL_CNF, "MNCC_REL_CNF" }, |
| 58 | { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" }, |
| 59 | { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" }, |
| 60 | { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" }, |
| 61 | { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" }, |
| 62 | { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" }, |
| 63 | { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" }, |
| 64 | { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" }, |
| 65 | { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" }, |
| 66 | { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" }, |
| 67 | { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" }, |
| 68 | { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" }, |
| 69 | { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" }, |
| 70 | { MNCC_HOLD_IND, "MNCC_HOLD_IND" }, |
| 71 | { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" }, |
| 72 | { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" }, |
| 73 | { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" }, |
| 74 | { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" }, |
| 75 | { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" }, |
| 76 | { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" }, |
| 77 | { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" }, |
| 78 | { MNCC_REJ_REQ, "MNCC_REJ_REQ" }, |
| 79 | { MNCC_REJ_IND, "MNCC_REJ_IND" }, |
| 80 | { MNCC_BRIDGE, "MNCC_BRIDGE" }, |
| 81 | { MNCC_FRAME_RECV, "MNCC_FRAME_RECV" }, |
| 82 | { MNCC_FRAME_DROP, "MNCC_FRAME_DROP" }, |
| 83 | { MNCC_LCHAN_MODIFY, "MNCC_LCHAN_MODIFY" }, |
| 84 | { MNCC_RTP_CREATE, "MNCC_RTP_CREATE" }, |
| 85 | { MNCC_RTP_CONNECT, "MNCC_RTP_CONNECT" }, |
| 86 | { MNCC_RTP_FREE, "MNCC_RTP_FREE" }, |
| 87 | { GSM_TCHF_FRAME, "GSM_TCHF_FRAME" }, |
| 88 | { GSM_TCHF_FRAME_EFR, "GSM_TCHF_FRAME_EFR" }, |
| 89 | { GSM_TCHH_FRAME, "GSM_TCHH_FRAME" }, |
| 90 | { GSM_TCH_FRAME_AMR, "GSM_TCH_FRAME_AMR" }, |
| 91 | { GSM_BAD_FRAME, "GSM_BAD_FRAME" }, |
| 92 | { 0, NULL }, |
| 93 | }; |
Harald Welte | 4bfdfe7 | 2009-06-10 23:11:52 +0800 | [diff] [blame] | 94 | |
Harald Welte | da8a19f | 2015-12-03 14:35:05 +0100 | [diff] [blame] | 95 | const char *get_mncc_name(int value) |
Harald Welte | 4bfdfe7 | 2009-06-10 23:11:52 +0800 | [diff] [blame] | 96 | { |
Harald Welte | da8a19f | 2015-12-03 14:35:05 +0100 | [diff] [blame] | 97 | return get_value_string(mncc_names, value); |
Harald Welte | 4bfdfe7 | 2009-06-10 23:11:52 +0800 | [diff] [blame] | 98 | } |
| 99 | |
Harald Welte | 4bfdfe7 | 2009-06-10 23:11:52 +0800 | [diff] [blame] | 100 | void mncc_set_cause(struct gsm_mncc *data, int loc, int val) |
| 101 | { |
| 102 | data->fields |= MNCC_F_CAUSE; |
| 103 | data->cause.location = loc; |
| 104 | data->cause.value = val; |
| 105 | } |
Harald Welte | fea236e | 2010-12-23 00:13:47 +0100 | [diff] [blame] | 106 | |
Harald Welte | 5718429 | 2018-01-22 01:49:02 +0100 | [diff] [blame] | 107 | |
| 108 | /*********************************************************************** |
| 109 | * MNCC validation code. Move to libosmocore once headers are merged |
| 110 | ************************************************************************/ |
| 111 | |
| 112 | #define MNCC_F_ALL 0x3fff |
| 113 | |
| 114 | static int check_string_terminated(const char *str, unsigned int size) |
| 115 | { |
| 116 | int i; |
| 117 | for (i = 0; i < size; i++) { |
| 118 | if (str[i] == 0) |
| 119 | return 0; |
| 120 | } |
| 121 | return -EINVAL; |
| 122 | } |
| 123 | |
| 124 | static int mncc_check_number(const struct gsm_mncc_number *num, const char *str) |
| 125 | { |
| 126 | int rc; |
| 127 | rc = check_string_terminated(num->number, ARRAY_SIZE(num->number)); |
| 128 | if (rc < 0) |
| 129 | LOGP(DMNCC, LOGL_ERROR, "MNCC %s number not terminated\n", str); |
| 130 | return rc; |
| 131 | } |
| 132 | |
| 133 | static int mncc_check_cause(const struct gsm_mncc_cause *cause) |
| 134 | { |
| 135 | if (cause->diag_len > sizeof(cause->diag)) |
| 136 | return -EINVAL; |
| 137 | return 0; |
| 138 | } |
| 139 | |
| 140 | static int mncc_check_useruser(const struct gsm_mncc_useruser *uu) |
| 141 | { |
| 142 | return check_string_terminated(uu->info, ARRAY_SIZE(uu->info)); |
| 143 | } |
| 144 | |
| 145 | static int mncc_check_facility(const struct gsm_mncc_facility *fac) |
| 146 | { |
| 147 | return check_string_terminated(fac->info, ARRAY_SIZE(fac->info)); |
| 148 | } |
| 149 | |
| 150 | static int mncc_check_ssversion(const struct gsm_mncc_ssversion *ssv) |
| 151 | { |
| 152 | return check_string_terminated(ssv->info, ARRAY_SIZE(ssv->info)); |
| 153 | } |
| 154 | |
| 155 | static int mncc_prim_check_sign(const struct gsm_mncc *mncc_prim) |
| 156 | { |
| 157 | int rc; |
| 158 | |
| 159 | if (mncc_prim->fields & ~ MNCC_F_ALL) { |
| 160 | LOGP(DMNCC, LOGL_ERROR, "Unknown MNCC field mask 0x%x\n", mncc_prim->fields); |
| 161 | return -EINVAL; |
| 162 | } |
| 163 | |
| 164 | rc = check_string_terminated(mncc_prim->imsi, sizeof(mncc_prim->imsi)); |
| 165 | if (rc < 0) { |
| 166 | LOGP(DMNCC, LOGL_ERROR, "MNCC IMSI not terminated\n"); |
| 167 | return rc; |
| 168 | } |
| 169 | |
| 170 | if (mncc_prim->fields & MNCC_F_CALLED) { |
| 171 | rc = mncc_check_number(&mncc_prim->called, "called"); |
| 172 | if (rc < 0) |
| 173 | return rc; |
| 174 | } |
| 175 | |
| 176 | if (mncc_prim->fields & MNCC_F_CALLING) { |
| 177 | rc = mncc_check_number(&mncc_prim->calling, "calling"); |
| 178 | if (rc < 0) |
| 179 | return rc; |
| 180 | } |
| 181 | |
| 182 | if (mncc_prim->fields & MNCC_F_REDIRECTING) { |
| 183 | rc = mncc_check_number(&mncc_prim->redirecting, "redirecting"); |
| 184 | if (rc < 0) |
| 185 | return rc; |
| 186 | } |
| 187 | |
| 188 | if (mncc_prim->fields & MNCC_F_CONNECTED) { |
| 189 | rc = mncc_check_number(&mncc_prim->connected, "connected"); |
| 190 | if (rc < 0) |
| 191 | return rc; |
| 192 | } |
| 193 | |
| 194 | if (mncc_prim->fields & MNCC_F_CAUSE) { |
| 195 | rc = mncc_check_cause(&mncc_prim->cause); |
| 196 | if (rc < 0) |
| 197 | return rc; |
| 198 | } |
| 199 | |
| 200 | if (mncc_prim->fields & MNCC_F_USERUSER) { |
| 201 | rc = mncc_check_useruser(&mncc_prim->useruser); |
| 202 | if (rc < 0) |
| 203 | return rc; |
| 204 | } |
| 205 | |
| 206 | if (mncc_prim->fields & MNCC_F_FACILITY) { |
| 207 | rc = mncc_check_facility(&mncc_prim->facility); |
| 208 | if (rc < 0) |
| 209 | return rc; |
| 210 | } |
| 211 | |
| 212 | if (mncc_prim->fields & MNCC_F_SSVERSION) { |
| 213 | rc = mncc_check_ssversion(&mncc_prim->ssversion); |
| 214 | if (rc < 0) |
| 215 | return rc; |
| 216 | } |
| 217 | |
| 218 | if (mncc_prim->fields & MNCC_F_BEARER_CAP) { |
| 219 | bool m1_found = false; |
| 220 | int i; |
| 221 | |
| 222 | for (i = 0; i < ARRAY_SIZE(mncc_prim->bearer_cap.speech_ver); i++) { |
| 223 | if (mncc_prim->bearer_cap.speech_ver[i] == -1) { |
| 224 | m1_found = true; |
| 225 | break; |
| 226 | } |
| 227 | } |
| 228 | if (!m1_found) { |
| 229 | LOGP(DMNCC, LOGL_ERROR, "Unterminated MNCC bearer capability\n"); |
| 230 | return -EINVAL; |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | return 0; |
| 235 | } |
| 236 | |
| 237 | int mncc_prim_check(const struct gsm_mncc *mncc_prim, unsigned int len) |
| 238 | { |
| 239 | if (len < sizeof(mncc_prim->msg_type)) { |
| 240 | LOGP(DMNCC, LOGL_ERROR, "Short MNCC Header\n"); |
| 241 | return -EINVAL; |
| 242 | } |
| 243 | |
| 244 | switch (mncc_prim->msg_type) { |
| 245 | case MNCC_SOCKET_HELLO: |
| 246 | if (len < sizeof(struct gsm_mncc_hello)) { |
| 247 | LOGP(DMNCC, LOGL_ERROR, "Short MNCC Hello\n"); |
| 248 | return -EINVAL; |
| 249 | } |
| 250 | break; |
| 251 | case GSM_BAD_FRAME: |
| 252 | case GSM_TCH_FRAME_AMR: |
| 253 | case GSM_TCHH_FRAME: |
| 254 | case GSM_TCHF_FRAME_EFR: |
| 255 | case GSM_TCHF_FRAME: |
| 256 | if (len < sizeof(struct gsm_data_frame)) { |
| 257 | LOGP(DMNCC, LOGL_ERROR, "Short MNCC TCH\n"); |
| 258 | return -EINVAL; |
| 259 | } |
| 260 | break; |
| 261 | case MNCC_RTP_FREE: |
| 262 | case MNCC_RTP_CONNECT: |
| 263 | case MNCC_RTP_CREATE: |
| 264 | if (len < sizeof(struct gsm_mncc_rtp)) { |
| 265 | LOGP(DMNCC, LOGL_ERROR, "Short MNCC RTP\n"); |
| 266 | return -EINVAL; |
| 267 | } |
| 268 | break; |
| 269 | case MNCC_LCHAN_MODIFY: |
| 270 | case MNCC_FRAME_DROP: |
| 271 | case MNCC_FRAME_RECV: |
| 272 | /* FIXME */ |
| 273 | break; |
| 274 | case MNCC_BRIDGE: |
| 275 | if (len < sizeof(struct gsm_mncc_bridge)) { |
| 276 | LOGP(DMNCC, LOGL_ERROR, "Short MNCC BRIDGE\n"); |
| 277 | return -EINVAL; |
| 278 | } |
| 279 | break; |
| 280 | default: |
| 281 | if (len < sizeof(struct gsm_mncc)) { |
| 282 | LOGP(DMNCC, LOGL_ERROR, "Short MNCC Signalling\n"); |
| 283 | return -EINVAL; |
| 284 | } |
| 285 | return mncc_prim_check_sign(mncc_prim); |
| 286 | } |
| 287 | return 0; |
| 288 | } |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 289 | |
| 290 | static uint8_t mncc_speech_ver_to_perm_speech(int speech_ver) |
| 291 | { |
| 292 | /* The speech versions that are transmitted in the Bearer capability |
| 293 | * information element, that is transmitted on the Layer 3 (CC) |
| 294 | * use a different encoding than the permitted speech version |
| 295 | * identifier, that is signalled in the channel type element on the A |
| 296 | * interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008, |
| 297 | * 10.5.103 */ |
| 298 | |
| 299 | switch (speech_ver) { |
| 300 | case GSM48_BCAP_SV_FR: |
| 301 | return GSM0808_PERM_FR1; |
| 302 | case GSM48_BCAP_SV_HR: |
| 303 | return GSM0808_PERM_HR1; |
| 304 | case GSM48_BCAP_SV_EFR: |
| 305 | return GSM0808_PERM_FR2; |
| 306 | case GSM48_BCAP_SV_AMR_F: |
| 307 | return GSM0808_PERM_FR3; |
| 308 | case GSM48_BCAP_SV_AMR_H: |
| 309 | return GSM0808_PERM_HR3; |
| 310 | case GSM48_BCAP_SV_AMR_OFW: |
| 311 | return GSM0808_PERM_FR4; |
| 312 | case GSM48_BCAP_SV_AMR_OHW: |
| 313 | return GSM0808_PERM_HR4; |
| 314 | case GSM48_BCAP_SV_AMR_FW: |
| 315 | return GSM0808_PERM_FR5; |
| 316 | case GSM48_BCAP_SV_AMR_OH: |
| 317 | return GSM0808_PERM_HR6; |
| 318 | } |
| 319 | |
| 320 | /* If nothing matches, tag the result as invalid */ |
| 321 | LOGP(DBSSAP, LOGL_ERROR, "Invalid permitted speech version: %d\n", speech_ver); |
| 322 | return 0xFF; |
| 323 | } |
| 324 | |
| 325 | /* Convert speech preference field */ |
| 326 | static uint8_t mncc_bc_radio_to_speech_pref(int radio) |
| 327 | { |
| 328 | /* The Radio channel requirement field that is transmitted in the |
| 329 | * Bearer capability information element, that is transmitted on the |
| 330 | * Layer 3 (CC) uses a different encoding than the Channel rate and |
| 331 | * type field that is signalled in the channel type element on the A |
| 332 | * interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008, |
| 333 | * 10.5.102 */ |
| 334 | |
| 335 | switch (radio) { |
| 336 | case GSM48_BCAP_RRQ_FR_ONLY: |
| 337 | return GSM0808_SPEECH_FULL_BM; |
| 338 | case GSM48_BCAP_RRQ_DUAL_FR: |
| 339 | return GSM0808_SPEECH_FULL_PREF; |
| 340 | case GSM48_BCAP_RRQ_DUAL_HR: |
| 341 | return GSM0808_SPEECH_HALF_PREF; |
| 342 | } |
| 343 | |
| 344 | LOGP(DBSSAP, LOGL_ERROR, "Invalid radio channel preference: %d; defaulting to full rate.\n", radio); |
| 345 | return GSM0808_SPEECH_FULL_BM; |
| 346 | } |
| 347 | |
| 348 | int mncc_bearer_cap_to_channel_type(struct gsm0808_channel_type *ct, const struct gsm_mncc_bearer_cap *bc) |
| 349 | { |
| 350 | unsigned int i; |
| 351 | uint8_t sv; |
| 352 | unsigned int count = 0; |
| 353 | bool only_gsm_hr = true; |
| 354 | |
| 355 | ct->ch_indctr = GSM0808_CHAN_SPEECH; |
| 356 | |
| 357 | for (i = 0; i < ARRAY_SIZE(bc->speech_ver); i++) { |
| 358 | if (bc->speech_ver[i] == -1) |
| 359 | break; |
| 360 | sv = mncc_speech_ver_to_perm_speech(bc->speech_ver[i]); |
| 361 | if (sv != 0xFF) { |
| 362 | /* Detect if something else than |
| 363 | * GSM HR V1 is supported */ |
| 364 | if (sv == GSM0808_PERM_HR2 || |
| 365 | sv == GSM0808_PERM_HR3 || sv == GSM0808_PERM_HR4 || sv == GSM0808_PERM_HR6) |
| 366 | only_gsm_hr = false; |
| 367 | |
| 368 | ct->perm_spch[count] = sv; |
| 369 | count++; |
| 370 | } |
| 371 | } |
| 372 | ct->perm_spch_len = count; |
| 373 | |
| 374 | if (only_gsm_hr) |
| 375 | /* Note: We must avoid the usage of GSM HR1 as this |
| 376 | * codec only offers very poor audio quality. If the |
| 377 | * MS only supports GSM HR1 (and full rate), and has |
| 378 | * a preference for half rate. Then we will ignore the |
| 379 | * preference and assume a preference for full rate. */ |
| 380 | ct->ch_rate_type = GSM0808_SPEECH_FULL_BM; |
| 381 | else |
| 382 | ct->ch_rate_type = mncc_bc_radio_to_speech_pref(bc->radio); |
| 383 | |
| 384 | if (count) |
| 385 | return 0; |
| 386 | else |
| 387 | return -EINVAL; |
| 388 | } |