blob: a5efc7312e596a511f66b597f7f01b23ca697be4 [file] [log] [blame]
Harald Welte4bfdfe72009-06-10 23:11:52 +08001/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
2 * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de>
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
Harald Welte09b7e7f2009-12-12 21:36:53 +010025#include <errno.h>
Harald Welte4bfdfe72009-06-10 23:11:52 +080026#include <sys/types.h>
27
28#include <openbsc/gsm_04_08.h>
29#include <openbsc/debug.h>
30#include <openbsc/mncc.h>
Harald Welte2cf161b2009-06-20 22:36:41 +020031#include <openbsc/talloc.h>
32#include <openbsc/gsm_data.h>
33
Harald Welte (local)d19e58b2009-08-15 02:30:58 +020034void *tall_call_ctx;
Harald Welte4bfdfe72009-06-10 23:11:52 +080035
36static struct mncc_names {
37 char *name;
38 int value;
39} mncc_names[] = {
40 {"MNCC_SETUP_REQ", 0x0101},
41 {"MNCC_SETUP_IND", 0x0102},
42 {"MNCC_SETUP_RSP", 0x0103},
43 {"MNCC_SETUP_CNF", 0x0104},
44 {"MNCC_SETUP_COMPL_REQ",0x0105},
45 {"MNCC_SETUP_COMPL_IND",0x0106},
46 {"MNCC_CALL_CONF_IND", 0x0107},
47 {"MNCC_CALL_PROC_REQ", 0x0108},
48 {"MNCC_PROGRESS_REQ", 0x0109},
49 {"MNCC_ALERT_REQ", 0x010a},
50 {"MNCC_ALERT_IND", 0x010b},
51 {"MNCC_NOTIFY_REQ", 0x010c},
52 {"MNCC_NOTIFY_IND", 0x010d},
53 {"MNCC_DISC_REQ", 0x010e},
54 {"MNCC_DISC_IND", 0x010f},
55 {"MNCC_REL_REQ", 0x0110},
56 {"MNCC_REL_IND", 0x0111},
57 {"MNCC_REL_CNF", 0x0112},
58 {"MNCC_FACILITY_REQ", 0x0113},
59 {"MNCC_FACILITY_IND", 0x0114},
60 {"MNCC_START_DTMF_IND", 0x0115},
61 {"MNCC_START_DTMF_RSP", 0x0116},
62 {"MNCC_START_DTMF_REJ", 0x0117},
63 {"MNCC_STOP_DTMF_IND", 0x0118},
64 {"MNCC_STOP_DTMF_RSP", 0x0119},
65 {"MNCC_MODIFY_REQ", 0x011a},
66 {"MNCC_MODIFY_IND", 0x011b},
67 {"MNCC_MODIFY_RSP", 0x011c},
68 {"MNCC_MODIFY_CNF", 0x011d},
69 {"MNCC_MODIFY_REJ", 0x011e},
70 {"MNCC_HOLD_IND", 0x011f},
71 {"MNCC_HOLD_CNF", 0x0120},
72 {"MNCC_HOLD_REJ", 0x0121},
73 {"MNCC_RETRIEVE_IND", 0x0122},
74 {"MNCC_RETRIEVE_CNF", 0x0123},
75 {"MNCC_RETRIEVE_REJ", 0x0124},
76 {"MNCC_USERINFO_REQ", 0x0125},
77 {"MNCC_USERINFO_IND", 0x0126},
78 {"MNCC_REJ_REQ", 0x0127},
79 {"MNCC_REJ_IND", 0x0128},
80
81 {"MNCC_BRIDGE", 0x0200},
82 {"MNCC_FRAME_RECV", 0x0201},
83 {"MNCC_FRAME_DROP", 0x0202},
84 {"MNCC_LCHAN_MODIFY", 0x0203},
85
86 {"GSM_TRAU_FRAME", 0x0300},
87
Harald Welteb1d4c8e2009-12-17 23:10:46 +010088 {NULL, 0} };
Harald Welte4bfdfe72009-06-10 23:11:52 +080089
90static LLIST_HEAD(call_list);
91
92static u_int32_t new_callref = 0x00000001;
93
94char *get_mncc_name(int value)
95{
96 int i;
97
98 for (i = 0; mncc_names[i].name; i++) {
99 if (mncc_names[i].value == value)
100 return mncc_names[i].name;
101 }
102
103 return "MNCC_Unknown";
104}
105
106static void free_call(struct gsm_call *call)
107{
108 llist_del(&call->entry);
109 DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
Harald Welte2cf161b2009-06-20 22:36:41 +0200110 talloc_free(call);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800111}
112
113
114struct gsm_call *get_call_ref(u_int32_t callref)
115{
116 struct gsm_call *callt;
117
118 llist_for_each_entry(callt, &call_list, entry) {
119 if (callt->callref == callref)
120 return callt;
121 }
122 return NULL;
123}
124
125void mncc_set_cause(struct gsm_mncc *data, int loc, int val)
126{
127 data->fields |= MNCC_F_CAUSE;
128 data->cause.location = loc;
129 data->cause.value = val;
130}
131
132/* on incoming call, look up database and send setup to remote subscr. */
133static int mncc_setup_ind(struct gsm_call *call, int msg_type,
134 struct gsm_mncc *setup)
135{
136 struct gsm_mncc mncc;
137 struct gsm_call *remote;
138
Harald Welte09b7e7f2009-12-12 21:36:53 +0100139 memset(&mncc, 0, sizeof(struct gsm_mncc));
140 mncc.callref = call->callref;
141
Harald Welte4bfdfe72009-06-10 23:11:52 +0800142 /* already have remote call */
143 if (call->remote_ref)
144 return 0;
145
Harald Welte09b7e7f2009-12-12 21:36:53 +0100146 /* transfer mode 1 would be packet mode, which was never specified */
147 if (setup->bearer_cap.mode != 0) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100148 LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support "
149 "packet mode\n", call->callref);
Harald Welte09b7e7f2009-12-12 21:36:53 +0100150 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
151 GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
152 goto out_reject;
153 }
154
155 /* we currently only do speech */
156 if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100157 LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support "
158 "voice calls\n", call->callref);
Harald Welte09b7e7f2009-12-12 21:36:53 +0100159 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
160 GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
161 goto out_reject;
162 }
163
Harald Welte4bfdfe72009-06-10 23:11:52 +0800164 /* create remote call */
Harald Welte2cf161b2009-06-20 22:36:41 +0200165 if (!(remote = talloc(tall_call_ctx, struct gsm_call))) {
Harald Weltec66b71c2009-06-11 14:23:20 +0800166 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
167 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Harald Welte09b7e7f2009-12-12 21:36:53 +0100168 goto out_reject;
Harald Welte4bfdfe72009-06-10 23:11:52 +0800169 }
170 llist_add_tail(&remote->entry, &call_list);
171 remote->net = call->net;
172 remote->callref = new_callref++;
173 DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n",
174 call->callref, remote->callref);
175
176 /* link remote call */
177 call->remote_ref = remote->callref;
178 remote->remote_ref = call->callref;
179
180 /* modify mode */
181 memset(&mncc, 0, sizeof(struct gsm_mncc));
182 mncc.callref = call->callref;
183 mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR;
184 DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref);
185 mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc);
186
187 /* send call proceeding */
188 memset(&mncc, 0, sizeof(struct gsm_mncc));
189 mncc.callref = call->callref;
190 DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref);
191 mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc);
192
193 /* send setup to remote */
194// setup->fields |= MNCC_F_SIGNAL;
195// setup->signal = GSM48_SIGNAL_DIALTONE;
196 setup->callref = remote->callref;
197 DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
198 return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
Harald Welte09b7e7f2009-12-12 21:36:53 +0100199
200out_reject:
201 mncc_send(call->net, MNCC_REJ_REQ, &mncc);
202 free_call(call);
203 return 0;
Harald Welte4bfdfe72009-06-10 23:11:52 +0800204}
205
206static int mncc_alert_ind(struct gsm_call *call, int msg_type,
207 struct gsm_mncc *alert)
208{
209 struct gsm_call *remote;
210
211 /* send alerting to remote */
212 if (!(remote = get_call_ref(call->remote_ref)))
213 return 0;
214 alert->callref = remote->callref;
215 DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref);
216 return mncc_send(remote->net, MNCC_ALERT_REQ, alert);
217}
218
219static int mncc_notify_ind(struct gsm_call *call, int msg_type,
220 struct gsm_mncc *notify)
221{
222 struct gsm_call *remote;
223
224 /* send notify to remote */
225 if (!(remote = get_call_ref(call->remote_ref)))
226 return 0;
227 notify->callref = remote->callref;
228 DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref);
229 return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify);
230}
231
232static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
233 struct gsm_mncc *connect)
234{
235 struct gsm_mncc connect_ack;
236 struct gsm_call *remote;
237 u_int32_t refs[2];
238
239 /* acknowledge connect */
240 memset(&connect_ack, 0, sizeof(struct gsm_mncc));
241 connect_ack.callref = call->callref;
242 DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
243 mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack);
244
245 /* send connect message to remote */
246 if (!(remote = get_call_ref(call->remote_ref)))
247 return 0;
248 connect->callref = remote->callref;
249 DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref);
250 mncc_send(remote->net, MNCC_SETUP_RSP, connect);
251
252 /* bridge tch */
253 refs[0] = call->callref;
254 refs[1] = call->remote_ref;
255 DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
256 return mncc_send(call->net, MNCC_BRIDGE, refs);
257}
258
259static int mncc_disc_ind(struct gsm_call *call, int msg_type,
260 struct gsm_mncc *disc)
261{
262 struct gsm_call *remote;
263
264 /* send release */
265 DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n",
266 call->callref, disc->cause.value);
267 mncc_send(call->net, MNCC_REL_REQ, disc);
268
269 /* send disc to remote */
270 if (!(remote = get_call_ref(call->remote_ref))) {
271 return 0;
272 }
273 disc->callref = remote->callref;
274 DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n",
275 remote->callref, disc->cause.value);
276 return mncc_send(remote->net, MNCC_DISC_REQ, disc);
277}
278
279static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
280{
281 struct gsm_call *remote;
282
283 /* send release to remote */
284 if (!(remote = get_call_ref(call->remote_ref))) {
285 free_call(call);
286 return 0;
287 }
288 rel->callref = remote->callref;
289 DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n",
290 call->callref, rel->cause.value);
291 mncc_send(remote->net, MNCC_REL_REQ, rel);
292
293 free_call(call);
294
295 return 0;
296}
297
298static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
299{
300 free_call(call);
301 return 0;
302}
303
304int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
305{
306 struct gsm_mncc *data = arg;
307 int callref;
308 struct gsm_call *call = NULL, *callt;
309 int rc = 0;
310
311 /* Special messages */
312 switch(msg_type) {
313 }
314
315 /* find callref */
316 callref = data->callref;
317 llist_for_each_entry(callt, &call_list, entry) {
318 if (callt->callref == callref) {
319 call = callt;
320 break;
321 }
322 }
323
324 /* create callref, if setup is received */
325 if (!call) {
326 if (msg_type != MNCC_SETUP_IND)
327 return 0; /* drop */
328 /* create call */
Harald Welte9b11e872009-06-26 19:42:28 +0200329 if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) {
Harald Welte4bfdfe72009-06-10 23:11:52 +0800330 struct gsm_mncc rel;
331
332 memset(&rel, 0, sizeof(struct gsm_mncc));
333 rel.callref = callref;
Harald Weltec66b71c2009-06-11 14:23:20 +0800334 mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
335 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800336 mncc_send(net, MNCC_REL_REQ, &rel);
337 return 0;
338 }
339 llist_add_tail(&call->entry, &call_list);
340 call->net = net;
341 call->callref = callref;
342 DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
343 }
344
345 DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
346 get_mncc_name(msg_type));
347
348 switch(msg_type) {
349 case MNCC_SETUP_IND:
350 rc = mncc_setup_ind(call, msg_type, arg);
351 break;
352 case MNCC_SETUP_CNF:
353 rc = mncc_setup_cnf(call, msg_type, arg);
354 break;
355 case MNCC_SETUP_COMPL_IND:
356 break;
357 case MNCC_CALL_CONF_IND:
358 /* we now need to MODIFY the channel */
359 data->lchan_mode = GSM48_CMODE_SPEECH_EFR;
360 mncc_send(call->net, MNCC_LCHAN_MODIFY, data);
361 break;
362 case MNCC_ALERT_IND:
363 rc = mncc_alert_ind(call, msg_type, arg);
364 break;
365 case MNCC_NOTIFY_IND:
366 rc = mncc_notify_ind(call, msg_type, arg);
367 break;
368 case MNCC_DISC_IND:
369 rc = mncc_disc_ind(call, msg_type, arg);
370 break;
371 case MNCC_REL_IND:
372 case MNCC_REJ_IND:
373 rc = mncc_rel_ind(call, msg_type, arg);
374 break;
375 case MNCC_REL_CNF:
376 rc = mncc_rel_cnf(call, msg_type, arg);
377 break;
378 case MNCC_FACILITY_IND:
379 break;
380 case MNCC_START_DTMF_IND:
381 break;
382 case MNCC_STOP_DTMF_IND:
383 break;
384 case MNCC_MODIFY_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800385 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
386 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800387 DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n",
388 call->callref, data->cause.value);
389 rc = mncc_send(net, MNCC_MODIFY_REJ, data);
390 break;
391 case MNCC_MODIFY_CNF:
392 break;
393 case MNCC_HOLD_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800394 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
395 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800396 DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n",
397 call->callref, data->cause.value);
398 rc = mncc_send(net, MNCC_HOLD_REJ, data);
399 break;
400 case MNCC_RETRIEVE_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800401 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
402 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800403 DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n",
404 call->callref, data->cause.value);
405 rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
406 break;
407 default:
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100408 LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800409 break;
410 }
411
412 return rc;
413}