blob: de17657615e592a9262f626e3e6ef0e9c243fccb [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
88 {NULL, 0}
89};
90
91static LLIST_HEAD(call_list);
92
93static u_int32_t new_callref = 0x00000001;
94
95char *get_mncc_name(int value)
96{
97 int i;
98
99 for (i = 0; mncc_names[i].name; i++) {
100 if (mncc_names[i].value == value)
101 return mncc_names[i].name;
102 }
103
104 return "MNCC_Unknown";
105}
106
107static void free_call(struct gsm_call *call)
108{
109 llist_del(&call->entry);
110 DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
Harald Welte2cf161b2009-06-20 22:36:41 +0200111 talloc_free(call);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800112}
113
114
115struct gsm_call *get_call_ref(u_int32_t callref)
116{
117 struct gsm_call *callt;
118
119 llist_for_each_entry(callt, &call_list, entry) {
120 if (callt->callref == callref)
121 return callt;
122 }
123 return NULL;
124}
125
126void mncc_set_cause(struct gsm_mncc *data, int loc, int val)
127{
128 data->fields |= MNCC_F_CAUSE;
129 data->cause.location = loc;
130 data->cause.value = val;
131}
132
133/* on incoming call, look up database and send setup to remote subscr. */
134static int mncc_setup_ind(struct gsm_call *call, int msg_type,
135 struct gsm_mncc *setup)
136{
137 struct gsm_mncc mncc;
138 struct gsm_call *remote;
139
Harald Welte09b7e7f2009-12-12 21:36:53 +0100140 memset(&mncc, 0, sizeof(struct gsm_mncc));
141 mncc.callref = call->callref;
142
Harald Welte4bfdfe72009-06-10 23:11:52 +0800143 /* already have remote call */
144 if (call->remote_ref)
145 return 0;
146
Harald Welte09b7e7f2009-12-12 21:36:53 +0100147 /* transfer mode 1 would be packet mode, which was never specified */
148 if (setup->bearer_cap.mode != 0) {
149 DEBUGP(DMNCC, "(call %x) We don't support packet mode\n",
150 call->callref);
151 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
152 GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
153 goto out_reject;
154 }
155
156 /* we currently only do speech */
157 if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) {
158 DEBUGP(DMNCC, "(call %x) We only support voice calls\n",
159 call->callref);
160 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
161 GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
162 goto out_reject;
163 }
164
Harald Welte4bfdfe72009-06-10 23:11:52 +0800165 /* create remote call */
Harald Welte2cf161b2009-06-20 22:36:41 +0200166 if (!(remote = talloc(tall_call_ctx, struct gsm_call))) {
Harald Weltec66b71c2009-06-11 14:23:20 +0800167 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
168 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Harald Welte09b7e7f2009-12-12 21:36:53 +0100169 goto out_reject;
Harald Welte4bfdfe72009-06-10 23:11:52 +0800170 }
171 llist_add_tail(&remote->entry, &call_list);
172 remote->net = call->net;
173 remote->callref = new_callref++;
174 DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n",
175 call->callref, remote->callref);
176
177 /* link remote call */
178 call->remote_ref = remote->callref;
179 remote->remote_ref = call->callref;
180
181 /* modify mode */
182 memset(&mncc, 0, sizeof(struct gsm_mncc));
183 mncc.callref = call->callref;
184 mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR;
185 DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref);
186 mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc);
187
188 /* send call proceeding */
189 memset(&mncc, 0, sizeof(struct gsm_mncc));
190 mncc.callref = call->callref;
191 DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref);
192 mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc);
193
194 /* send setup to remote */
195// setup->fields |= MNCC_F_SIGNAL;
196// setup->signal = GSM48_SIGNAL_DIALTONE;
197 setup->callref = remote->callref;
198 DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
199 return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
Harald Welte09b7e7f2009-12-12 21:36:53 +0100200
201out_reject:
202 mncc_send(call->net, MNCC_REJ_REQ, &mncc);
203 free_call(call);
204 return 0;
Harald Welte4bfdfe72009-06-10 23:11:52 +0800205}
206
207static int mncc_alert_ind(struct gsm_call *call, int msg_type,
208 struct gsm_mncc *alert)
209{
210 struct gsm_call *remote;
211
212 /* send alerting to remote */
213 if (!(remote = get_call_ref(call->remote_ref)))
214 return 0;
215 alert->callref = remote->callref;
216 DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref);
217 return mncc_send(remote->net, MNCC_ALERT_REQ, alert);
218}
219
220static int mncc_notify_ind(struct gsm_call *call, int msg_type,
221 struct gsm_mncc *notify)
222{
223 struct gsm_call *remote;
224
225 /* send notify to remote */
226 if (!(remote = get_call_ref(call->remote_ref)))
227 return 0;
228 notify->callref = remote->callref;
229 DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref);
230 return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify);
231}
232
233static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
234 struct gsm_mncc *connect)
235{
236 struct gsm_mncc connect_ack;
237 struct gsm_call *remote;
238 u_int32_t refs[2];
239
240 /* acknowledge connect */
241 memset(&connect_ack, 0, sizeof(struct gsm_mncc));
242 connect_ack.callref = call->callref;
243 DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
244 mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack);
245
246 /* send connect message to remote */
247 if (!(remote = get_call_ref(call->remote_ref)))
248 return 0;
249 connect->callref = remote->callref;
250 DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref);
251 mncc_send(remote->net, MNCC_SETUP_RSP, connect);
252
253 /* bridge tch */
254 refs[0] = call->callref;
255 refs[1] = call->remote_ref;
256 DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
257 return mncc_send(call->net, MNCC_BRIDGE, refs);
258}
259
260static int mncc_disc_ind(struct gsm_call *call, int msg_type,
261 struct gsm_mncc *disc)
262{
263 struct gsm_call *remote;
264
265 /* send release */
266 DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n",
267 call->callref, disc->cause.value);
268 mncc_send(call->net, MNCC_REL_REQ, disc);
269
270 /* send disc to remote */
271 if (!(remote = get_call_ref(call->remote_ref))) {
272 return 0;
273 }
274 disc->callref = remote->callref;
275 DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n",
276 remote->callref, disc->cause.value);
277 return mncc_send(remote->net, MNCC_DISC_REQ, disc);
278}
279
280static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
281{
282 struct gsm_call *remote;
283
284 /* send release to remote */
285 if (!(remote = get_call_ref(call->remote_ref))) {
286 free_call(call);
287 return 0;
288 }
289 rel->callref = remote->callref;
290 DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n",
291 call->callref, rel->cause.value);
292 mncc_send(remote->net, MNCC_REL_REQ, rel);
293
294 free_call(call);
295
296 return 0;
297}
298
299static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
300{
301 free_call(call);
302 return 0;
303}
304
305int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
306{
307 struct gsm_mncc *data = arg;
308 int callref;
309 struct gsm_call *call = NULL, *callt;
310 int rc = 0;
311
312 /* Special messages */
313 switch(msg_type) {
314 }
315
316 /* find callref */
317 callref = data->callref;
318 llist_for_each_entry(callt, &call_list, entry) {
319 if (callt->callref == callref) {
320 call = callt;
321 break;
322 }
323 }
324
325 /* create callref, if setup is received */
326 if (!call) {
327 if (msg_type != MNCC_SETUP_IND)
328 return 0; /* drop */
329 /* create call */
Harald Welte9b11e872009-06-26 19:42:28 +0200330 if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) {
Harald Welte4bfdfe72009-06-10 23:11:52 +0800331 struct gsm_mncc rel;
332
333 memset(&rel, 0, sizeof(struct gsm_mncc));
334 rel.callref = callref;
Harald Weltec66b71c2009-06-11 14:23:20 +0800335 mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
336 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800337 mncc_send(net, MNCC_REL_REQ, &rel);
338 return 0;
339 }
340 llist_add_tail(&call->entry, &call_list);
341 call->net = net;
342 call->callref = callref;
343 DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
344 }
345
346 DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
347 get_mncc_name(msg_type));
348
349 switch(msg_type) {
350 case MNCC_SETUP_IND:
351 rc = mncc_setup_ind(call, msg_type, arg);
352 break;
353 case MNCC_SETUP_CNF:
354 rc = mncc_setup_cnf(call, msg_type, arg);
355 break;
356 case MNCC_SETUP_COMPL_IND:
357 break;
358 case MNCC_CALL_CONF_IND:
359 /* we now need to MODIFY the channel */
360 data->lchan_mode = GSM48_CMODE_SPEECH_EFR;
361 mncc_send(call->net, MNCC_LCHAN_MODIFY, data);
362 break;
363 case MNCC_ALERT_IND:
364 rc = mncc_alert_ind(call, msg_type, arg);
365 break;
366 case MNCC_NOTIFY_IND:
367 rc = mncc_notify_ind(call, msg_type, arg);
368 break;
369 case MNCC_DISC_IND:
370 rc = mncc_disc_ind(call, msg_type, arg);
371 break;
372 case MNCC_REL_IND:
373 case MNCC_REJ_IND:
374 rc = mncc_rel_ind(call, msg_type, arg);
375 break;
376 case MNCC_REL_CNF:
377 rc = mncc_rel_cnf(call, msg_type, arg);
378 break;
379 case MNCC_FACILITY_IND:
380 break;
381 case MNCC_START_DTMF_IND:
382 break;
383 case MNCC_STOP_DTMF_IND:
384 break;
385 case MNCC_MODIFY_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800386 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
387 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800388 DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n",
389 call->callref, data->cause.value);
390 rc = mncc_send(net, MNCC_MODIFY_REJ, data);
391 break;
392 case MNCC_MODIFY_CNF:
393 break;
394 case MNCC_HOLD_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800395 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
396 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800397 DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n",
398 call->callref, data->cause.value);
399 rc = mncc_send(net, MNCC_HOLD_REJ, data);
400 break;
401 case MNCC_RETRIEVE_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800402 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
403 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800404 DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n",
405 call->callref, data->cause.value);
406 rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
407 break;
408 default:
409 DEBUGP(DMNCC, "(call %x) Message unhandled\n", callref);
410 break;
411 }
412
413 return rc;
414}