blob: 575497654a47ca86484fa279607a1e8e78e4934a [file] [log] [blame]
Harald Welte3e6376d2010-12-22 23:54:51 +01001/* mncc_builtin.c - default, minimal built-in MNCC Application for
Alexander Couzensfbd96f52016-08-29 18:40:02 +02002 * standalone bsc_hack (network-in-the-box mode) */
Harald Welte3e6376d2010-12-22 23:54:51 +01003
4/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
5 * (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
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <errno.h>
Harald Welte3e6376d2010-12-22 23:54:51 +010029
Neels Hofmeyr90843962017-09-04 15:04:35 +020030#include <osmocom/msc/gsm_04_08.h>
31#include <osmocom/msc/debug.h>
32#include <osmocom/msc/mncc.h>
33#include <osmocom/msc/mncc_int.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010034#include <osmocom/core/talloc.h>
Neels Hofmeyr90843962017-09-04 15:04:35 +020035#include <osmocom/msc/gsm_data.h>
36#include <osmocom/msc/transaction.h>
Harald Welte3e6376d2010-12-22 23:54:51 +010037
Max3332ac42018-12-17 11:17:33 +010038#define DEBUGCC(l, r, fmt, args...) DEBUGP(DMNCC, "(call %x, remote %x) " fmt, l->callref, r->callref, ##args)
39
Harald Welte3e6376d2010-12-22 23:54:51 +010040void *tall_call_ctx;
41
42static LLIST_HEAD(call_list);
43
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020044static uint32_t new_callref = 0x00000001;
Harald Welte3e6376d2010-12-22 23:54:51 +010045
Harald Welteab386e62011-09-01 18:18:43 +020046struct mncc_int mncc_int = {
Andreas Eversberga83d5112013-12-07 18:32:28 +010047 .def_codec = { GSM48_CMODE_SPEECH_V1, GSM48_CMODE_SPEECH_V1 },
Harald Welteab386e62011-09-01 18:18:43 +020048};
49
Harald Welte3e6376d2010-12-22 23:54:51 +010050static void free_call(struct gsm_call *call)
51{
52 llist_del(&call->entry);
53 DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
54 talloc_free(call);
55}
56
57
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020058static struct gsm_call *get_call_ref(uint32_t callref)
Harald Welte3e6376d2010-12-22 23:54:51 +010059{
60 struct gsm_call *callt;
61
62 llist_for_each_entry(callt, &call_list, entry) {
63 if (callt->callref == callref)
64 return callt;
65 }
66 return NULL;
67}
68
Harald Welte3e6376d2010-12-22 23:54:51 +010069/* on incoming call, look up database and send setup to remote subscr. */
Neels Hofmeyr52558742019-05-09 01:23:09 +020070static int mncc_setup_ind(struct gsm_call *call,
Harald Welte3e6376d2010-12-22 23:54:51 +010071 struct gsm_mncc *setup)
72{
73 struct gsm_mncc mncc;
74 struct gsm_call *remote;
75
76 memset(&mncc, 0, sizeof(struct gsm_mncc));
77 mncc.callref = call->callref;
78
79 /* already have remote call */
80 if (call->remote_ref)
81 return 0;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010082
Harald Welte3e6376d2010-12-22 23:54:51 +010083 /* transfer mode 1 would be packet mode, which was never specified */
84 if (setup->bearer_cap.mode != 0) {
85 LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support "
86 "packet mode\n", call->callref);
87 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
88 GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
89 goto out_reject;
90 }
91
92 /* we currently only do speech */
93 if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) {
94 LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support "
95 "voice calls\n", call->callref);
96 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
97 GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
98 goto out_reject;
99 }
100
101 /* create remote call */
Andreas Eversbergc5e08512013-01-25 08:36:32 +0100102 if (!(remote = talloc_zero(tall_call_ctx, struct gsm_call))) {
Harald Welte3e6376d2010-12-22 23:54:51 +0100103 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
104 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
105 goto out_reject;
106 }
107 llist_add_tail(&remote->entry, &call_list);
108 remote->net = call->net;
109 remote->callref = new_callref++;
Max3332ac42018-12-17 11:17:33 +0100110 DEBUGCC(call, remote, "Creating new remote instance.\n");
Harald Welte3e6376d2010-12-22 23:54:51 +0100111
112 /* link remote call */
113 call->remote_ref = remote->callref;
114 remote->remote_ref = call->callref;
115
Harald Weltebf0a7c92012-08-24 16:48:21 +0200116 /* send call proceeding */
117 memset(&mncc, 0, sizeof(struct gsm_mncc));
118 mncc.callref = call->callref;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200119 mncc.msg_type = MNCC_CALL_PROC_REQ;
Max3332ac42018-12-17 11:17:33 +0100120 DEBUGCC(call, remote, "Accepting call.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200121 mncc_tx_to_cc(call->net, &mncc);
Harald Weltebf0a7c92012-08-24 16:48:21 +0200122
Harald Welte3e6376d2010-12-22 23:54:51 +0100123 /* modify mode */
124 memset(&mncc, 0, sizeof(struct gsm_mncc));
125 mncc.callref = call->callref;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200126 mncc.msg_type = MNCC_LCHAN_MODIFY;
Max3332ac42018-12-17 11:17:33 +0100127 DEBUGCC(call, remote, "Modify channel mode.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200128 mncc_tx_to_cc(call->net, &mncc);
Harald Welte3e6376d2010-12-22 23:54:51 +0100129
Harald Welte3e6376d2010-12-22 23:54:51 +0100130 /* send setup to remote */
131// setup->fields |= MNCC_F_SIGNAL;
132// setup->signal = GSM48_SIGNAL_DIALTONE;
133 setup->callref = remote->callref;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200134 setup->msg_type = MNCC_SETUP_REQ;
Max3332ac42018-12-17 11:17:33 +0100135 DEBUGCC(call, remote, "Forwarding SETUP to remote.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200136 return mncc_tx_to_cc(remote->net, setup);
Harald Welte3e6376d2010-12-22 23:54:51 +0100137
138out_reject:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200139 mncc.msg_type = MNCC_REJ_REQ;
140 mncc_tx_to_cc(call->net, &mncc);
Harald Welte3e6376d2010-12-22 23:54:51 +0100141 free_call(call);
142 return 0;
143}
144
Neels Hofmeyr52558742019-05-09 01:23:09 +0200145static int mncc_alert_ind(struct gsm_call *call,
Harald Welte3e6376d2010-12-22 23:54:51 +0100146 struct gsm_mncc *alert)
147{
148 struct gsm_call *remote;
149
150 /* send alerting to remote */
151 if (!(remote = get_call_ref(call->remote_ref)))
152 return 0;
153 alert->callref = remote->callref;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200154 alert->msg_type = MNCC_ALERT_REQ;
Max3332ac42018-12-17 11:17:33 +0100155 DEBUGCC(call, remote, "Forwarding ALERT to remote.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200156 return mncc_tx_to_cc(remote->net, alert);
Harald Welte3e6376d2010-12-22 23:54:51 +0100157}
158
Neels Hofmeyr52558742019-05-09 01:23:09 +0200159static int mncc_notify_ind(struct gsm_call *call,
Harald Welte3e6376d2010-12-22 23:54:51 +0100160 struct gsm_mncc *notify)
161{
162 struct gsm_call *remote;
163
164 /* send notify to remote */
165 if (!(remote = get_call_ref(call->remote_ref)))
166 return 0;
167 notify->callref = remote->callref;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200168 notify->msg_type = MNCC_NOTIFY_REQ;
Max3332ac42018-12-17 11:17:33 +0100169 DEBUGCC(call, remote, "Forwarding NOTIF to remote.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200170 return mncc_tx_to_cc(remote->net, notify);
Harald Welte3e6376d2010-12-22 23:54:51 +0100171}
172
Neels Hofmeyr52558742019-05-09 01:23:09 +0200173static int mncc_setup_cnf(struct gsm_call *call,
Harald Welte3e6376d2010-12-22 23:54:51 +0100174 struct gsm_mncc *connect)
175{
Neels Hofmeyrf9773b02018-11-29 23:01:58 +0100176 struct gsm_mncc connect_ack;
Harald Welte3e6376d2010-12-22 23:54:51 +0100177 struct gsm_call *remote;
Harald Welte53d51f52015-12-03 14:59:04 +0100178 struct gsm_mncc_bridge bridge = { .msg_type = MNCC_BRIDGE };
Harald Welte3e6376d2010-12-22 23:54:51 +0100179
180 /* acknowledge connect */
181 memset(&connect_ack, 0, sizeof(struct gsm_mncc));
Neels Hofmeyr52558742019-05-09 01:23:09 +0200182 connect_ack.msg_type = MNCC_SETUP_COMPL_REQ;
Harald Welte3e6376d2010-12-22 23:54:51 +0100183 connect_ack.callref = call->callref;
184 DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200185 mncc_tx_to_cc(call->net, &connect_ack);
Harald Welte3e6376d2010-12-22 23:54:51 +0100186
187 /* send connect message to remote */
188 if (!(remote = get_call_ref(call->remote_ref)))
189 return 0;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200190 connect->msg_type = MNCC_SETUP_RSP;
Harald Welte3e6376d2010-12-22 23:54:51 +0100191 connect->callref = remote->callref;
Max3332ac42018-12-17 11:17:33 +0100192 DEBUGCC(call, remote, "Sending CONNECT to remote.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200193 mncc_tx_to_cc(remote->net, connect);
Harald Welte3e6376d2010-12-22 23:54:51 +0100194
195 /* bridge tch */
Neels Hofmeyr52558742019-05-09 01:23:09 +0200196 bridge.msg_type = MNCC_BRIDGE;
Harald Welte53d51f52015-12-03 14:59:04 +0100197 bridge.callref[0] = call->callref;
198 bridge.callref[1] = call->remote_ref;
Max3332ac42018-12-17 11:17:33 +0100199 DEBUGCC(call, remote, "Bridging with remote.\n");
Harald Welte3e6376d2010-12-22 23:54:51 +0100200
Neels Hofmeyr52558742019-05-09 01:23:09 +0200201 return mncc_tx_to_cc(call->net, &bridge);
Harald Welte3e6376d2010-12-22 23:54:51 +0100202}
203
Neels Hofmeyr52558742019-05-09 01:23:09 +0200204static int mncc_disc_ind(struct gsm_call *call,
Harald Welte3e6376d2010-12-22 23:54:51 +0100205 struct gsm_mncc *disc)
206{
207 struct gsm_call *remote;
208
209 /* send release */
210 DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n",
211 call->callref, disc->cause.value);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200212 disc->msg_type = MNCC_REL_REQ;
213 mncc_tx_to_cc(call->net, disc);
Harald Welte3e6376d2010-12-22 23:54:51 +0100214
215 /* send disc to remote */
216 if (!(remote = get_call_ref(call->remote_ref))) {
217 return 0;
218 }
Neels Hofmeyr52558742019-05-09 01:23:09 +0200219 disc->msg_type = MNCC_DISC_REQ;
Harald Welte3e6376d2010-12-22 23:54:51 +0100220 disc->callref = remote->callref;
Max3332ac42018-12-17 11:17:33 +0100221 DEBUGCC(call, remote, "Disconnecting remote with cause %d\n", disc->cause.value);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200222 return mncc_tx_to_cc(remote->net, disc);
Harald Welte3e6376d2010-12-22 23:54:51 +0100223}
224
Neels Hofmeyr52558742019-05-09 01:23:09 +0200225static int mncc_rel_ind(struct gsm_call *call, struct gsm_mncc *rel)
Harald Welte3e6376d2010-12-22 23:54:51 +0100226{
227 struct gsm_call *remote;
228
229 /* send release to remote */
230 if (!(remote = get_call_ref(call->remote_ref))) {
231 free_call(call);
232 return 0;
233 }
Holger Hans Peter Freythera61c7092011-01-27 15:05:45 +0100234
Neels Hofmeyr52558742019-05-09 01:23:09 +0200235 rel->msg_type = MNCC_REL_REQ;
Harald Welte3e6376d2010-12-22 23:54:51 +0100236 rel->callref = remote->callref;
Max3332ac42018-12-17 11:17:33 +0100237 DEBUGCC(call, remote, "Releasing remote with cause %d\n", rel->cause.value);
Harald Welte3e6376d2010-12-22 23:54:51 +0100238
Holger Hans Peter Freythera61c7092011-01-27 15:05:45 +0100239 /*
240 * Release this side of the call right now. Otherwise we end up
241 * in this method for the other call and will also try to release
242 * it and then we will end up with a double free and a crash
243 */
Harald Welte3e6376d2010-12-22 23:54:51 +0100244 free_call(call);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200245 mncc_tx_to_cc(remote->net, rel);
Harald Welte3e6376d2010-12-22 23:54:51 +0100246
247 return 0;
248}
249
Neels Hofmeyr52558742019-05-09 01:23:09 +0200250static int mncc_rel_cnf(struct gsm_call *call, struct gsm_mncc *rel)
Harald Welte3e6376d2010-12-22 23:54:51 +0100251{
252 free_call(call);
253 return 0;
254}
255
Harald Welte3e6376d2010-12-22 23:54:51 +0100256/* Internal MNCC handler input function (from CC -> MNCC -> here) */
Harald Welte29b64e92010-12-23 01:07:46 +0100257int int_mncc_recv(struct gsm_network *net, struct msgb *msg)
Harald Welte3e6376d2010-12-22 23:54:51 +0100258{
Harald Welte29b64e92010-12-23 01:07:46 +0100259 void *arg = msgb_data(msg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100260 struct gsm_mncc *data = arg;
Harald Welte29b64e92010-12-23 01:07:46 +0100261 int msg_type = data->msg_type;
Harald Welte3e6376d2010-12-22 23:54:51 +0100262 int callref;
263 struct gsm_call *call = NULL, *callt;
264 int rc = 0;
265
266 /* Special messages */
267 switch(msg_type) {
268 }
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100269
Harald Welte3e6376d2010-12-22 23:54:51 +0100270 /* find callref */
271 callref = data->callref;
272 llist_for_each_entry(callt, &call_list, entry) {
273 if (callt->callref == callref) {
274 call = callt;
275 break;
276 }
277 }
278
279 /* create callref, if setup is received */
280 if (!call) {
281 if (msg_type != MNCC_SETUP_IND)
Harald Welte29b64e92010-12-23 01:07:46 +0100282 goto out_free; /* drop */
Harald Welte3e6376d2010-12-22 23:54:51 +0100283 /* create call */
284 if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) {
285 struct gsm_mncc rel;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100286
Harald Welte3e6376d2010-12-22 23:54:51 +0100287 memset(&rel, 0, sizeof(struct gsm_mncc));
Neels Hofmeyr52558742019-05-09 01:23:09 +0200288 rel.msg_type = MNCC_REL_REQ;
Harald Welte3e6376d2010-12-22 23:54:51 +0100289 rel.callref = callref;
290 mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
291 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200292 mncc_tx_to_cc(net, &rel);
Harald Welte29b64e92010-12-23 01:07:46 +0100293 goto out_free;
Harald Welte3e6376d2010-12-22 23:54:51 +0100294 }
295 llist_add_tail(&call->entry, &call_list);
296 call->net = net;
297 call->callref = callref;
298 DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
299 }
300
Andreas Eversberg9acbe4c2013-03-11 08:12:43 +0100301 if (mncc_is_data_frame(msg_type)) {
Neels Hofmeyre2f24d52017-05-08 15:12:20 +0200302 LOGP(DMNCC, LOGL_ERROR, "(call %x) Received data frame, which is not supported.\n",
303 call->callref);
Andreas Eversberg9acbe4c2013-03-11 08:12:43 +0100304 goto out_free;
Harald Welte3e6376d2010-12-22 23:54:51 +0100305 }
306
Andreas Eversberg9acbe4c2013-03-11 08:12:43 +0100307 DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
308 get_mncc_name(msg_type));
309
Harald Welte3e6376d2010-12-22 23:54:51 +0100310 switch(msg_type) {
311 case MNCC_SETUP_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200312 rc = mncc_setup_ind(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100313 break;
314 case MNCC_SETUP_CNF:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200315 rc = mncc_setup_cnf(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100316 break;
317 case MNCC_SETUP_COMPL_IND:
318 break;
319 case MNCC_CALL_CONF_IND:
320 /* we now need to MODIFY the channel */
Neels Hofmeyr52558742019-05-09 01:23:09 +0200321 data->msg_type = MNCC_LCHAN_MODIFY;
322 mncc_tx_to_cc(call->net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100323 break;
324 case MNCC_ALERT_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200325 rc = mncc_alert_ind(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100326 break;
327 case MNCC_NOTIFY_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200328 rc = mncc_notify_ind(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100329 break;
330 case MNCC_DISC_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200331 rc = mncc_disc_ind(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100332 break;
333 case MNCC_REL_IND:
334 case MNCC_REJ_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200335 rc = mncc_rel_ind(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100336 break;
337 case MNCC_REL_CNF:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200338 rc = mncc_rel_cnf(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100339 break;
340 case MNCC_FACILITY_IND:
341 break;
342 case MNCC_START_DTMF_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200343 data->msg_type = MNCC_START_DTMF_REJ;
344 rc = mncc_tx_to_cc(net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100345 break;
346 case MNCC_STOP_DTMF_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200347 data->msg_type = MNCC_STOP_DTMF_RSP;
348 rc = mncc_tx_to_cc(net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100349 break;
350 case MNCC_MODIFY_IND:
351 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
352 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
353 DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n",
354 call->callref, data->cause.value);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200355 data->msg_type = MNCC_MODIFY_REJ;
356 rc = mncc_tx_to_cc(net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100357 break;
358 case MNCC_MODIFY_CNF:
359 break;
360 case MNCC_HOLD_IND:
361 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
362 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
363 DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n",
364 call->callref, data->cause.value);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200365 data->msg_type = MNCC_HOLD_REJ;
366 rc = mncc_tx_to_cc(net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100367 break;
368 case MNCC_RETRIEVE_IND:
369 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
370 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
371 DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n",
372 call->callref, data->cause.value);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200373 data->msg_type = MNCC_RETRIEVE_REJ;
374 rc = mncc_tx_to_cc(net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100375 break;
Harald Welte3e6376d2010-12-22 23:54:51 +0100376 default:
377 LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref);
378 break;
379 }
380
Harald Welte29b64e92010-12-23 01:07:46 +0100381out_free:
Holger Hans Peter Freythera8ddb082012-03-01 20:30:32 +0100382 msgb_free(msg);
Harald Welte29b64e92010-12-23 01:07:46 +0100383
Harald Welte3e6376d2010-12-22 23:54:51 +0100384 return rc;
385}