blob: 71f2ad5018c44e67e7fbfce0a7d455a9ab664931 [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>
25#include <sys/types.h>
26
27#include <openbsc/gsm_04_08.h>
28#include <openbsc/debug.h>
29#include <openbsc/mncc.h>
Harald Welte2cf161b2009-06-20 22:36:41 +020030#include <openbsc/talloc.h>
31#include <openbsc/gsm_data.h>
32
33static void *tall_call_ctx;
Harald Welte4bfdfe72009-06-10 23:11:52 +080034
35static struct mncc_names {
36 char *name;
37 int value;
38} mncc_names[] = {
39 {"MNCC_SETUP_REQ", 0x0101},
40 {"MNCC_SETUP_IND", 0x0102},
41 {"MNCC_SETUP_RSP", 0x0103},
42 {"MNCC_SETUP_CNF", 0x0104},
43 {"MNCC_SETUP_COMPL_REQ",0x0105},
44 {"MNCC_SETUP_COMPL_IND",0x0106},
45 {"MNCC_CALL_CONF_IND", 0x0107},
46 {"MNCC_CALL_PROC_REQ", 0x0108},
47 {"MNCC_PROGRESS_REQ", 0x0109},
48 {"MNCC_ALERT_REQ", 0x010a},
49 {"MNCC_ALERT_IND", 0x010b},
50 {"MNCC_NOTIFY_REQ", 0x010c},
51 {"MNCC_NOTIFY_IND", 0x010d},
52 {"MNCC_DISC_REQ", 0x010e},
53 {"MNCC_DISC_IND", 0x010f},
54 {"MNCC_REL_REQ", 0x0110},
55 {"MNCC_REL_IND", 0x0111},
56 {"MNCC_REL_CNF", 0x0112},
57 {"MNCC_FACILITY_REQ", 0x0113},
58 {"MNCC_FACILITY_IND", 0x0114},
59 {"MNCC_START_DTMF_IND", 0x0115},
60 {"MNCC_START_DTMF_RSP", 0x0116},
61 {"MNCC_START_DTMF_REJ", 0x0117},
62 {"MNCC_STOP_DTMF_IND", 0x0118},
63 {"MNCC_STOP_DTMF_RSP", 0x0119},
64 {"MNCC_MODIFY_REQ", 0x011a},
65 {"MNCC_MODIFY_IND", 0x011b},
66 {"MNCC_MODIFY_RSP", 0x011c},
67 {"MNCC_MODIFY_CNF", 0x011d},
68 {"MNCC_MODIFY_REJ", 0x011e},
69 {"MNCC_HOLD_IND", 0x011f},
70 {"MNCC_HOLD_CNF", 0x0120},
71 {"MNCC_HOLD_REJ", 0x0121},
72 {"MNCC_RETRIEVE_IND", 0x0122},
73 {"MNCC_RETRIEVE_CNF", 0x0123},
74 {"MNCC_RETRIEVE_REJ", 0x0124},
75 {"MNCC_USERINFO_REQ", 0x0125},
76 {"MNCC_USERINFO_IND", 0x0126},
77 {"MNCC_REJ_REQ", 0x0127},
78 {"MNCC_REJ_IND", 0x0128},
79
80 {"MNCC_BRIDGE", 0x0200},
81 {"MNCC_FRAME_RECV", 0x0201},
82 {"MNCC_FRAME_DROP", 0x0202},
83 {"MNCC_LCHAN_MODIFY", 0x0203},
84
85 {"GSM_TRAU_FRAME", 0x0300},
86
87 {NULL, 0}
88};
89
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
139 /* already have remote call */
140 if (call->remote_ref)
141 return 0;
142
Harald Welte2cf161b2009-06-20 22:36:41 +0200143 if (!tall_call_ctx)
144 tall_call_ctx = talloc_named_const(tall_bsc_ctx, 1,
145 "gsm_call");
Harald Welte4bfdfe72009-06-10 23:11:52 +0800146 /* create remote call */
Harald Welte2cf161b2009-06-20 22:36:41 +0200147 if (!(remote = talloc(tall_call_ctx, struct gsm_call))) {
Harald Welte4bfdfe72009-06-10 23:11:52 +0800148 memset(&mncc, 0, sizeof(struct gsm_mncc));
149 mncc.callref = call->callref;
Harald Weltec66b71c2009-06-11 14:23:20 +0800150 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
151 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800152 mncc_send(call->net, MNCC_REJ_REQ, &mncc);
153 free_call(call);
154 return 0;
155 }
156 llist_add_tail(&remote->entry, &call_list);
157 remote->net = call->net;
158 remote->callref = new_callref++;
159 DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n",
160 call->callref, remote->callref);
161
162 /* link remote call */
163 call->remote_ref = remote->callref;
164 remote->remote_ref = call->callref;
165
166 /* modify mode */
167 memset(&mncc, 0, sizeof(struct gsm_mncc));
168 mncc.callref = call->callref;
169 mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR;
170 DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref);
171 mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc);
172
173 /* send call proceeding */
174 memset(&mncc, 0, sizeof(struct gsm_mncc));
175 mncc.callref = call->callref;
176 DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref);
177 mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc);
178
179 /* send setup to remote */
180// setup->fields |= MNCC_F_SIGNAL;
181// setup->signal = GSM48_SIGNAL_DIALTONE;
182 setup->callref = remote->callref;
183 DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
184 return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
185}
186
187static int mncc_alert_ind(struct gsm_call *call, int msg_type,
188 struct gsm_mncc *alert)
189{
190 struct gsm_call *remote;
191
192 /* send alerting to remote */
193 if (!(remote = get_call_ref(call->remote_ref)))
194 return 0;
195 alert->callref = remote->callref;
196 DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref);
197 return mncc_send(remote->net, MNCC_ALERT_REQ, alert);
198}
199
200static int mncc_notify_ind(struct gsm_call *call, int msg_type,
201 struct gsm_mncc *notify)
202{
203 struct gsm_call *remote;
204
205 /* send notify to remote */
206 if (!(remote = get_call_ref(call->remote_ref)))
207 return 0;
208 notify->callref = remote->callref;
209 DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref);
210 return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify);
211}
212
213static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
214 struct gsm_mncc *connect)
215{
216 struct gsm_mncc connect_ack;
217 struct gsm_call *remote;
218 u_int32_t refs[2];
219
220 /* acknowledge connect */
221 memset(&connect_ack, 0, sizeof(struct gsm_mncc));
222 connect_ack.callref = call->callref;
223 DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
224 mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack);
225
226 /* send connect message to remote */
227 if (!(remote = get_call_ref(call->remote_ref)))
228 return 0;
229 connect->callref = remote->callref;
230 DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref);
231 mncc_send(remote->net, MNCC_SETUP_RSP, connect);
232
233 /* bridge tch */
234 refs[0] = call->callref;
235 refs[1] = call->remote_ref;
236 DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
237 return mncc_send(call->net, MNCC_BRIDGE, refs);
238}
239
240static int mncc_disc_ind(struct gsm_call *call, int msg_type,
241 struct gsm_mncc *disc)
242{
243 struct gsm_call *remote;
244
245 /* send release */
246 DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n",
247 call->callref, disc->cause.value);
248 mncc_send(call->net, MNCC_REL_REQ, disc);
249
250 /* send disc to remote */
251 if (!(remote = get_call_ref(call->remote_ref))) {
252 return 0;
253 }
254 disc->callref = remote->callref;
255 DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n",
256 remote->callref, disc->cause.value);
257 return mncc_send(remote->net, MNCC_DISC_REQ, disc);
258}
259
260static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
261{
262 struct gsm_call *remote;
263
264 /* send release to remote */
265 if (!(remote = get_call_ref(call->remote_ref))) {
266 free_call(call);
267 return 0;
268 }
269 rel->callref = remote->callref;
270 DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n",
271 call->callref, rel->cause.value);
272 mncc_send(remote->net, MNCC_REL_REQ, rel);
273
274 free_call(call);
275
276 return 0;
277}
278
279static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
280{
281 free_call(call);
282 return 0;
283}
284
285int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
286{
287 struct gsm_mncc *data = arg;
288 int callref;
289 struct gsm_call *call = NULL, *callt;
290 int rc = 0;
291
292 /* Special messages */
293 switch(msg_type) {
294 }
295
296 /* find callref */
297 callref = data->callref;
298 llist_for_each_entry(callt, &call_list, entry) {
299 if (callt->callref == callref) {
300 call = callt;
301 break;
302 }
303 }
304
305 /* create callref, if setup is received */
306 if (!call) {
307 if (msg_type != MNCC_SETUP_IND)
308 return 0; /* drop */
Harald Welte2cf161b2009-06-20 22:36:41 +0200309 if (!tall_call_ctx)
310 tall_call_ctx = talloc_named_const(tall_bsc_ctx, 1,
311 "gsm_call");
Harald Welte4bfdfe72009-06-10 23:11:52 +0800312 /* create call */
Harald Welte2cf161b2009-06-20 22:36:41 +0200313 if (!(call = talloc(tall_call_ctx, struct gsm_call))) {
Harald Welte4bfdfe72009-06-10 23:11:52 +0800314 struct gsm_mncc rel;
315
316 memset(&rel, 0, sizeof(struct gsm_mncc));
317 rel.callref = callref;
Harald Weltec66b71c2009-06-11 14:23:20 +0800318 mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
319 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800320 mncc_send(net, MNCC_REL_REQ, &rel);
321 return 0;
322 }
323 llist_add_tail(&call->entry, &call_list);
324 call->net = net;
325 call->callref = callref;
326 DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
327 }
328
329 DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
330 get_mncc_name(msg_type));
331
332 switch(msg_type) {
333 case MNCC_SETUP_IND:
334 rc = mncc_setup_ind(call, msg_type, arg);
335 break;
336 case MNCC_SETUP_CNF:
337 rc = mncc_setup_cnf(call, msg_type, arg);
338 break;
339 case MNCC_SETUP_COMPL_IND:
340 break;
341 case MNCC_CALL_CONF_IND:
342 /* we now need to MODIFY the channel */
343 data->lchan_mode = GSM48_CMODE_SPEECH_EFR;
344 mncc_send(call->net, MNCC_LCHAN_MODIFY, data);
345 break;
346 case MNCC_ALERT_IND:
347 rc = mncc_alert_ind(call, msg_type, arg);
348 break;
349 case MNCC_NOTIFY_IND:
350 rc = mncc_notify_ind(call, msg_type, arg);
351 break;
352 case MNCC_DISC_IND:
353 rc = mncc_disc_ind(call, msg_type, arg);
354 break;
355 case MNCC_REL_IND:
356 case MNCC_REJ_IND:
357 rc = mncc_rel_ind(call, msg_type, arg);
358 break;
359 case MNCC_REL_CNF:
360 rc = mncc_rel_cnf(call, msg_type, arg);
361 break;
362 case MNCC_FACILITY_IND:
363 break;
364 case MNCC_START_DTMF_IND:
365 break;
366 case MNCC_STOP_DTMF_IND:
367 break;
368 case MNCC_MODIFY_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800369 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
370 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800371 DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n",
372 call->callref, data->cause.value);
373 rc = mncc_send(net, MNCC_MODIFY_REJ, data);
374 break;
375 case MNCC_MODIFY_CNF:
376 break;
377 case MNCC_HOLD_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800378 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
379 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800380 DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n",
381 call->callref, data->cause.value);
382 rc = mncc_send(net, MNCC_HOLD_REJ, data);
383 break;
384 case MNCC_RETRIEVE_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 RETRIEVE with cause %d\n",
388 call->callref, data->cause.value);
389 rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
390 break;
391 default:
392 DEBUGP(DMNCC, "(call %x) Message unhandled\n", callref);
393 break;
394 }
395
396 return rc;
397}