blob: 026dae02545d1607244f05d99b101b0a9646e505 [file] [log] [blame]
Harald Welte3e6376d2010-12-22 23:54:51 +01001/* mncc.c - utility routines for the MNCC API between the 04.08
2 * message parsing and the actual Call Control logic */
Harald Welte7ce5e252010-12-22 02:02:48 +01003
Harald Welte57184292018-01-22 01:49:02 +01004/* (C) 2008-2018 by Harald Welte <laforge@gnumonks.org>
Harald Welte4bfdfe72009-06-10 23:11:52 +08005 * (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 Welte9af6ddf2011-01-01 15:25:50 +01009 * 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 Welte4bfdfe72009-06-10 23:11:52 +080011 * (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 Welte9af6ddf2011-01-01 15:25:50 +010016 * GNU Affero General Public License for more details.
Harald Welte4bfdfe72009-06-10 23:11:52 +080017 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010018 * 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 Welte4bfdfe72009-06-10 23:11:52 +080020 *
21 */
22
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
Harald Welte09b7e7f2009-12-12 21:36:53 +010027#include <errno.h>
Harald Welte4bfdfe72009-06-10 23:11:52 +080028
Harald Welteda8a19f2015-12-03 14:35:05 +010029#include <osmocom/core/talloc.h>
30#include <osmocom/core/utils.h>
31
Neels Hofmeyr90843962017-09-04 15:04:35 +020032#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 Welte2cf161b2009-06-20 22:36:41 +020037
Harald Welte4bfdfe72009-06-10 23:11:52 +080038
Harald Welteda8a19f2015-12-03 14:35:05 +010039static 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 Welte4bfdfe72009-06-10 23:11:52 +080094
Harald Welteda8a19f2015-12-03 14:35:05 +010095const char *get_mncc_name(int value)
Harald Welte4bfdfe72009-06-10 23:11:52 +080096{
Harald Welteda8a19f2015-12-03 14:35:05 +010097 return get_value_string(mncc_names, value);
Harald Welte4bfdfe72009-06-10 23:11:52 +080098}
99
Harald Welte4bfdfe72009-06-10 23:11:52 +0800100void mncc_set_cause(struct gsm_mncc *data, int loc, int val)
101{
102 data->fields |= MNCC_F_CAUSE;
Keith Whyteff17f8f2019-08-01 12:20:25 +0200103 data->cause.coding = GSM48_CAUSE_CODING_GSM;
Harald Welte4bfdfe72009-06-10 23:11:52 +0800104 data->cause.location = loc;
105 data->cause.value = val;
106}
Harald Weltefea236e2010-12-23 00:13:47 +0100107
Harald Welte57184292018-01-22 01:49:02 +0100108
109/***********************************************************************
110 * MNCC validation code. Move to libosmocore once headers are merged
111 ************************************************************************/
112
Harald Welte57184292018-01-22 01:49:02 +0100113static int check_string_terminated(const char *str, unsigned int size)
114{
115 int i;
116 for (i = 0; i < size; i++) {
117 if (str[i] == 0)
118 return 0;
119 }
120 return -EINVAL;
121}
122
123static int mncc_check_number(const struct gsm_mncc_number *num, const char *str)
124{
125 int rc;
126 rc = check_string_terminated(num->number, ARRAY_SIZE(num->number));
127 if (rc < 0)
128 LOGP(DMNCC, LOGL_ERROR, "MNCC %s number not terminated\n", str);
129 return rc;
130}
131
132static int mncc_check_cause(const struct gsm_mncc_cause *cause)
133{
134 if (cause->diag_len > sizeof(cause->diag))
135 return -EINVAL;
136 return 0;
137}
138
139static int mncc_check_useruser(const struct gsm_mncc_useruser *uu)
140{
141 return check_string_terminated(uu->info, ARRAY_SIZE(uu->info));
142}
143
144static int mncc_check_facility(const struct gsm_mncc_facility *fac)
145{
146 return check_string_terminated(fac->info, ARRAY_SIZE(fac->info));
147}
148
149static int mncc_check_ssversion(const struct gsm_mncc_ssversion *ssv)
150{
151 return check_string_terminated(ssv->info, ARRAY_SIZE(ssv->info));
152}
153
154static int mncc_prim_check_sign(const struct gsm_mncc *mncc_prim)
155{
156 int rc;
157
Vadim Yanitskiyeab86612022-10-03 16:19:24 +0700158 if (mncc_prim->fields & ~MNCC_F_ALL) {
Harald Welte57184292018-01-22 01:49:02 +0100159 LOGP(DMNCC, LOGL_ERROR, "Unknown MNCC field mask 0x%x\n", mncc_prim->fields);
160 return -EINVAL;
161 }
162
163 rc = check_string_terminated(mncc_prim->imsi, sizeof(mncc_prim->imsi));
164 if (rc < 0) {
165 LOGP(DMNCC, LOGL_ERROR, "MNCC IMSI not terminated\n");
166 return rc;
167 }
168
169 if (mncc_prim->fields & MNCC_F_CALLED) {
170 rc = mncc_check_number(&mncc_prim->called, "called");
171 if (rc < 0)
172 return rc;
173 }
174
175 if (mncc_prim->fields & MNCC_F_CALLING) {
176 rc = mncc_check_number(&mncc_prim->calling, "calling");
177 if (rc < 0)
178 return rc;
179 }
180
181 if (mncc_prim->fields & MNCC_F_REDIRECTING) {
182 rc = mncc_check_number(&mncc_prim->redirecting, "redirecting");
183 if (rc < 0)
184 return rc;
185 }
186
187 if (mncc_prim->fields & MNCC_F_CONNECTED) {
188 rc = mncc_check_number(&mncc_prim->connected, "connected");
189 if (rc < 0)
190 return rc;
191 }
192
193 if (mncc_prim->fields & MNCC_F_CAUSE) {
194 rc = mncc_check_cause(&mncc_prim->cause);
195 if (rc < 0)
196 return rc;
197 }
198
199 if (mncc_prim->fields & MNCC_F_USERUSER) {
200 rc = mncc_check_useruser(&mncc_prim->useruser);
201 if (rc < 0)
202 return rc;
203 }
204
205 if (mncc_prim->fields & MNCC_F_FACILITY) {
206 rc = mncc_check_facility(&mncc_prim->facility);
207 if (rc < 0)
208 return rc;
209 }
210
211 if (mncc_prim->fields & MNCC_F_SSVERSION) {
212 rc = mncc_check_ssversion(&mncc_prim->ssversion);
213 if (rc < 0)
214 return rc;
215 }
216
217 if (mncc_prim->fields & MNCC_F_BEARER_CAP) {
218 bool m1_found = false;
219 int i;
220
221 for (i = 0; i < ARRAY_SIZE(mncc_prim->bearer_cap.speech_ver); i++) {
222 if (mncc_prim->bearer_cap.speech_ver[i] == -1) {
223 m1_found = true;
224 break;
225 }
226 }
227 if (!m1_found) {
228 LOGP(DMNCC, LOGL_ERROR, "Unterminated MNCC bearer capability\n");
229 return -EINVAL;
230 }
231 }
232
233 return 0;
234}
235
Neels Hofmeyrb4552052019-10-21 03:00:26 +0200236/* Make sure that the SDP section has a terminating \0. The MNCC message may end after that \0, and if SDP is omitted it
237 * must contain at least one \0 byte. */
238int mncc_check_sdp_termination(const char *label, const struct gsm_mncc *mncc, unsigned int len, const char *sdp)
239{
240 size_t sdp_offset;
241 size_t sdp_data_len;
242 size_t sdp_str_len;
243
244 OSMO_ASSERT(((char*)mncc) < sdp);
245
246 sdp_offset = sdp - (char*)mncc;
247 if (len < sdp_offset)
248 goto too_short;
249
250 sdp_data_len = len - sdp_offset;
251 if (sdp_data_len < 1)
252 goto too_short;
253
254 sdp_str_len = strnlen(sdp, sdp_data_len);
255 /* There must be a \0, so sdp_str_len must be at most sdp_data_len - 1 */
256 if (sdp_str_len >= sdp_data_len)
257 goto too_short;
258 return 0;
259too_short:
260 LOGP(DMNCC, LOGL_ERROR, "Short %s\n", label);
261 return -EINVAL;
262}
263
Harald Welte57184292018-01-22 01:49:02 +0100264int mncc_prim_check(const struct gsm_mncc *mncc_prim, unsigned int len)
265{
266 if (len < sizeof(mncc_prim->msg_type)) {
267 LOGP(DMNCC, LOGL_ERROR, "Short MNCC Header\n");
268 return -EINVAL;
269 }
270
271 switch (mncc_prim->msg_type) {
272 case MNCC_SOCKET_HELLO:
273 if (len < sizeof(struct gsm_mncc_hello)) {
274 LOGP(DMNCC, LOGL_ERROR, "Short MNCC Hello\n");
275 return -EINVAL;
276 }
277 break;
278 case GSM_BAD_FRAME:
279 case GSM_TCH_FRAME_AMR:
280 case GSM_TCHH_FRAME:
281 case GSM_TCHF_FRAME_EFR:
282 case GSM_TCHF_FRAME:
283 if (len < sizeof(struct gsm_data_frame)) {
284 LOGP(DMNCC, LOGL_ERROR, "Short MNCC TCH\n");
285 return -EINVAL;
286 }
287 break;
288 case MNCC_RTP_FREE:
289 case MNCC_RTP_CONNECT:
290 case MNCC_RTP_CREATE:
Neels Hofmeyrb4552052019-10-21 03:00:26 +0200291 return mncc_check_sdp_termination("MNCC RTP", mncc_prim, len, ((struct gsm_mncc_rtp*)mncc_prim)->sdp);
Harald Welte57184292018-01-22 01:49:02 +0100292 case MNCC_LCHAN_MODIFY:
293 case MNCC_FRAME_DROP:
294 case MNCC_FRAME_RECV:
295 /* FIXME */
296 break;
297 case MNCC_BRIDGE:
298 if (len < sizeof(struct gsm_mncc_bridge)) {
299 LOGP(DMNCC, LOGL_ERROR, "Short MNCC BRIDGE\n");
300 return -EINVAL;
301 }
302 break;
303 default:
Neels Hofmeyrb4552052019-10-21 03:00:26 +0200304 if (mncc_check_sdp_termination("MNCC Signalling", mncc_prim, len, mncc_prim->sdp))
Harald Welte57184292018-01-22 01:49:02 +0100305 return -EINVAL;
Harald Welte57184292018-01-22 01:49:02 +0100306 return mncc_prim_check_sign(mncc_prim);
307 }
308 return 0;
309}
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100310
311static uint8_t mncc_speech_ver_to_perm_speech(int speech_ver)
312{
313 /* The speech versions that are transmitted in the Bearer capability
314 * information element, that is transmitted on the Layer 3 (CC)
315 * use a different encoding than the permitted speech version
316 * identifier, that is signalled in the channel type element on the A
317 * interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008,
318 * 10.5.103 */
319
320 switch (speech_ver) {
321 case GSM48_BCAP_SV_FR:
322 return GSM0808_PERM_FR1;
323 case GSM48_BCAP_SV_HR:
324 return GSM0808_PERM_HR1;
325 case GSM48_BCAP_SV_EFR:
326 return GSM0808_PERM_FR2;
327 case GSM48_BCAP_SV_AMR_F:
328 return GSM0808_PERM_FR3;
329 case GSM48_BCAP_SV_AMR_H:
330 return GSM0808_PERM_HR3;
331 case GSM48_BCAP_SV_AMR_OFW:
332 return GSM0808_PERM_FR4;
333 case GSM48_BCAP_SV_AMR_OHW:
334 return GSM0808_PERM_HR4;
335 case GSM48_BCAP_SV_AMR_FW:
336 return GSM0808_PERM_FR5;
337 case GSM48_BCAP_SV_AMR_OH:
338 return GSM0808_PERM_HR6;
339 }
340
341 /* If nothing matches, tag the result as invalid */
342 LOGP(DBSSAP, LOGL_ERROR, "Invalid permitted speech version: %d\n", speech_ver);
343 return 0xFF;
344}
345
346/* Convert speech preference field */
347static uint8_t mncc_bc_radio_to_speech_pref(int radio)
348{
349 /* The Radio channel requirement field that is transmitted in the
350 * Bearer capability information element, that is transmitted on the
351 * Layer 3 (CC) uses a different encoding than the Channel rate and
352 * type field that is signalled in the channel type element on the A
353 * interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008,
354 * 10.5.102 */
355
356 switch (radio) {
357 case GSM48_BCAP_RRQ_FR_ONLY:
358 return GSM0808_SPEECH_FULL_BM;
359 case GSM48_BCAP_RRQ_DUAL_FR:
360 return GSM0808_SPEECH_FULL_PREF;
361 case GSM48_BCAP_RRQ_DUAL_HR:
362 return GSM0808_SPEECH_HALF_PREF;
363 }
364
365 LOGP(DBSSAP, LOGL_ERROR, "Invalid radio channel preference: %d; defaulting to full rate.\n", radio);
366 return GSM0808_SPEECH_FULL_BM;
367}
368
369int mncc_bearer_cap_to_channel_type(struct gsm0808_channel_type *ct, const struct gsm_mncc_bearer_cap *bc)
370{
371 unsigned int i;
372 uint8_t sv;
373 unsigned int count = 0;
374 bool only_gsm_hr = true;
375
376 ct->ch_indctr = GSM0808_CHAN_SPEECH;
377
378 for (i = 0; i < ARRAY_SIZE(bc->speech_ver); i++) {
379 if (bc->speech_ver[i] == -1)
380 break;
381 sv = mncc_speech_ver_to_perm_speech(bc->speech_ver[i]);
382 if (sv != 0xFF) {
383 /* Detect if something else than
384 * GSM HR V1 is supported */
385 if (sv == GSM0808_PERM_HR2 ||
386 sv == GSM0808_PERM_HR3 || sv == GSM0808_PERM_HR4 || sv == GSM0808_PERM_HR6)
387 only_gsm_hr = false;
388
389 ct->perm_spch[count] = sv;
390 count++;
391 }
392 }
393 ct->perm_spch_len = count;
394
395 if (only_gsm_hr)
396 /* Note: We must avoid the usage of GSM HR1 as this
397 * codec only offers very poor audio quality. If the
398 * MS only supports GSM HR1 (and full rate), and has
399 * a preference for half rate. Then we will ignore the
400 * preference and assume a preference for full rate. */
401 ct->ch_rate_type = GSM0808_SPEECH_FULL_BM;
402 else
403 ct->ch_rate_type = mncc_bc_radio_to_speech_pref(bc->radio);
404
405 if (count)
406 return 0;
407 else
408 return -EINVAL;
409}