blob: f62541c05f2a45735650a1220af4669c438c8f82 [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
Harald Welte (local)d19e58b2009-08-15 02:30:58 +020033void *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
143 /* create remote call */
Harald Welte2cf161b2009-06-20 22:36:41 +0200144 if (!(remote = talloc(tall_call_ctx, struct gsm_call))) {
Harald Welte4bfdfe72009-06-10 23:11:52 +0800145 memset(&mncc, 0, sizeof(struct gsm_mncc));
146 mncc.callref = call->callref;
Harald Weltec66b71c2009-06-11 14:23:20 +0800147 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
148 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800149 mncc_send(call->net, MNCC_REJ_REQ, &mncc);
150 free_call(call);
151 return 0;
152 }
153 llist_add_tail(&remote->entry, &call_list);
154 remote->net = call->net;
155 remote->callref = new_callref++;
156 DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n",
157 call->callref, remote->callref);
158
159 /* link remote call */
160 call->remote_ref = remote->callref;
161 remote->remote_ref = call->callref;
162
163 /* modify mode */
164 memset(&mncc, 0, sizeof(struct gsm_mncc));
165 mncc.callref = call->callref;
166 mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR;
167 DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref);
168 mncc_send(call->net, MNCC_LCHAN_MODIFY, &mncc);
169
170 /* send call proceeding */
171 memset(&mncc, 0, sizeof(struct gsm_mncc));
172 mncc.callref = call->callref;
173 DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref);
174 mncc_send(call->net, MNCC_CALL_PROC_REQ, &mncc);
175
176 /* send setup to remote */
177// setup->fields |= MNCC_F_SIGNAL;
178// setup->signal = GSM48_SIGNAL_DIALTONE;
179 setup->callref = remote->callref;
180 DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
181 return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
182}
183
184static int mncc_alert_ind(struct gsm_call *call, int msg_type,
185 struct gsm_mncc *alert)
186{
187 struct gsm_call *remote;
188
189 /* send alerting to remote */
190 if (!(remote = get_call_ref(call->remote_ref)))
191 return 0;
192 alert->callref = remote->callref;
193 DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref);
194 return mncc_send(remote->net, MNCC_ALERT_REQ, alert);
195}
196
197static int mncc_notify_ind(struct gsm_call *call, int msg_type,
198 struct gsm_mncc *notify)
199{
200 struct gsm_call *remote;
201
202 /* send notify to remote */
203 if (!(remote = get_call_ref(call->remote_ref)))
204 return 0;
205 notify->callref = remote->callref;
206 DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref);
207 return mncc_send(remote->net, MNCC_NOTIFY_REQ, notify);
208}
209
210static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
211 struct gsm_mncc *connect)
212{
213 struct gsm_mncc connect_ack;
214 struct gsm_call *remote;
215 u_int32_t refs[2];
216
217 /* acknowledge connect */
218 memset(&connect_ack, 0, sizeof(struct gsm_mncc));
219 connect_ack.callref = call->callref;
220 DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
221 mncc_send(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack);
222
223 /* send connect message to remote */
224 if (!(remote = get_call_ref(call->remote_ref)))
225 return 0;
226 connect->callref = remote->callref;
227 DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref);
228 mncc_send(remote->net, MNCC_SETUP_RSP, connect);
229
230 /* bridge tch */
231 refs[0] = call->callref;
232 refs[1] = call->remote_ref;
233 DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
234 return mncc_send(call->net, MNCC_BRIDGE, refs);
235}
236
237static int mncc_disc_ind(struct gsm_call *call, int msg_type,
238 struct gsm_mncc *disc)
239{
240 struct gsm_call *remote;
241
242 /* send release */
243 DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n",
244 call->callref, disc->cause.value);
245 mncc_send(call->net, MNCC_REL_REQ, disc);
246
247 /* send disc to remote */
248 if (!(remote = get_call_ref(call->remote_ref))) {
249 return 0;
250 }
251 disc->callref = remote->callref;
252 DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n",
253 remote->callref, disc->cause.value);
254 return mncc_send(remote->net, MNCC_DISC_REQ, disc);
255}
256
257static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
258{
259 struct gsm_call *remote;
260
261 /* send release to remote */
262 if (!(remote = get_call_ref(call->remote_ref))) {
263 free_call(call);
264 return 0;
265 }
266 rel->callref = remote->callref;
267 DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n",
268 call->callref, rel->cause.value);
269 mncc_send(remote->net, MNCC_REL_REQ, rel);
270
271 free_call(call);
272
273 return 0;
274}
275
276static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel)
277{
278 free_call(call);
279 return 0;
280}
281
282int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
283{
284 struct gsm_mncc *data = arg;
285 int callref;
286 struct gsm_call *call = NULL, *callt;
287 int rc = 0;
288
289 /* Special messages */
290 switch(msg_type) {
291 }
292
293 /* find callref */
294 callref = data->callref;
295 llist_for_each_entry(callt, &call_list, entry) {
296 if (callt->callref == callref) {
297 call = callt;
298 break;
299 }
300 }
301
302 /* create callref, if setup is received */
303 if (!call) {
304 if (msg_type != MNCC_SETUP_IND)
305 return 0; /* drop */
306 /* create call */
Harald Welte9b11e872009-06-26 19:42:28 +0200307 if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) {
Harald Welte4bfdfe72009-06-10 23:11:52 +0800308 struct gsm_mncc rel;
309
310 memset(&rel, 0, sizeof(struct gsm_mncc));
311 rel.callref = callref;
Harald Weltec66b71c2009-06-11 14:23:20 +0800312 mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
313 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800314 mncc_send(net, MNCC_REL_REQ, &rel);
315 return 0;
316 }
317 llist_add_tail(&call->entry, &call_list);
318 call->net = net;
319 call->callref = callref;
320 DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
321 }
322
323 DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
324 get_mncc_name(msg_type));
325
326 switch(msg_type) {
327 case MNCC_SETUP_IND:
328 rc = mncc_setup_ind(call, msg_type, arg);
329 break;
330 case MNCC_SETUP_CNF:
331 rc = mncc_setup_cnf(call, msg_type, arg);
332 break;
333 case MNCC_SETUP_COMPL_IND:
334 break;
335 case MNCC_CALL_CONF_IND:
336 /* we now need to MODIFY the channel */
337 data->lchan_mode = GSM48_CMODE_SPEECH_EFR;
338 mncc_send(call->net, MNCC_LCHAN_MODIFY, data);
339 break;
340 case MNCC_ALERT_IND:
341 rc = mncc_alert_ind(call, msg_type, arg);
342 break;
343 case MNCC_NOTIFY_IND:
344 rc = mncc_notify_ind(call, msg_type, arg);
345 break;
346 case MNCC_DISC_IND:
347 rc = mncc_disc_ind(call, msg_type, arg);
348 break;
349 case MNCC_REL_IND:
350 case MNCC_REJ_IND:
351 rc = mncc_rel_ind(call, msg_type, arg);
352 break;
353 case MNCC_REL_CNF:
354 rc = mncc_rel_cnf(call, msg_type, arg);
355 break;
356 case MNCC_FACILITY_IND:
357 break;
358 case MNCC_START_DTMF_IND:
359 break;
360 case MNCC_STOP_DTMF_IND:
361 break;
362 case MNCC_MODIFY_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800363 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
364 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800365 DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n",
366 call->callref, data->cause.value);
367 rc = mncc_send(net, MNCC_MODIFY_REJ, data);
368 break;
369 case MNCC_MODIFY_CNF:
370 break;
371 case MNCC_HOLD_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800372 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
373 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800374 DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n",
375 call->callref, data->cause.value);
376 rc = mncc_send(net, MNCC_HOLD_REJ, data);
377 break;
378 case MNCC_RETRIEVE_IND:
Harald Weltec66b71c2009-06-11 14:23:20 +0800379 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
380 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
Harald Welte4bfdfe72009-06-10 23:11:52 +0800381 DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n",
382 call->callref, data->cause.value);
383 rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
384 break;
385 default:
386 DEBUGP(DMNCC, "(call %x) Message unhandled\n", callref);
387 break;
388 }
389
390 return rc;
391}