blob: a711e07425dd65a817e1ae240731ad40465f50e9 [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 *
Harald Welte3e6376d2010-12-22 23:54:51 +010018 */
19
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <errno.h>
Harald Welte3e6376d2010-12-22 23:54:51 +010025
Neels Hofmeyr90843962017-09-04 15:04:35 +020026#include <osmocom/msc/gsm_04_08.h>
27#include <osmocom/msc/debug.h>
28#include <osmocom/msc/mncc.h>
29#include <osmocom/msc/mncc_int.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010030#include <osmocom/core/talloc.h>
Neels Hofmeyr90843962017-09-04 15:04:35 +020031#include <osmocom/msc/gsm_data.h>
32#include <osmocom/msc/transaction.h>
Harald Welte3e6376d2010-12-22 23:54:51 +010033
Max3332ac42018-12-17 11:17:33 +010034#define DEBUGCC(l, r, fmt, args...) DEBUGP(DMNCC, "(call %x, remote %x) " fmt, l->callref, r->callref, ##args)
35
Harald Welte3e6376d2010-12-22 23:54:51 +010036void *tall_call_ctx;
37
38static LLIST_HEAD(call_list);
39
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020040static uint32_t new_callref = 0x00000001;
Harald Welte3e6376d2010-12-22 23:54:51 +010041
Harald Welteab386e62011-09-01 18:18:43 +020042struct mncc_int mncc_int = {
Andreas Eversberga83d5112013-12-07 18:32:28 +010043 .def_codec = { GSM48_CMODE_SPEECH_V1, GSM48_CMODE_SPEECH_V1 },
Harald Welteab386e62011-09-01 18:18:43 +020044};
45
Harald Welte3e6376d2010-12-22 23:54:51 +010046static void free_call(struct gsm_call *call)
47{
48 llist_del(&call->entry);
49 DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
50 talloc_free(call);
51}
52
53
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020054static struct gsm_call *get_call_ref(uint32_t callref)
Harald Welte3e6376d2010-12-22 23:54:51 +010055{
56 struct gsm_call *callt;
57
58 llist_for_each_entry(callt, &call_list, entry) {
59 if (callt->callref == callref)
60 return callt;
61 }
62 return NULL;
63}
64
Harald Welte3e6376d2010-12-22 23:54:51 +010065/* on incoming call, look up database and send setup to remote subscr. */
Neels Hofmeyr52558742019-05-09 01:23:09 +020066static int mncc_setup_ind(struct gsm_call *call,
Harald Welte3e6376d2010-12-22 23:54:51 +010067 struct gsm_mncc *setup)
68{
69 struct gsm_mncc mncc;
70 struct gsm_call *remote;
71
72 memset(&mncc, 0, sizeof(struct gsm_mncc));
73 mncc.callref = call->callref;
74
75 /* already have remote call */
76 if (call->remote_ref)
77 return 0;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010078
Harald Welte3e6376d2010-12-22 23:54:51 +010079 /* transfer mode 1 would be packet mode, which was never specified */
80 if (setup->bearer_cap.mode != 0) {
81 LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support "
82 "packet mode\n", call->callref);
83 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
84 GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
85 goto out_reject;
86 }
87
88 /* we currently only do speech */
89 if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) {
90 LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support "
91 "voice calls\n", call->callref);
92 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
93 GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
94 goto out_reject;
95 }
96
97 /* create remote call */
Andreas Eversbergc5e08512013-01-25 08:36:32 +010098 if (!(remote = talloc_zero(tall_call_ctx, struct gsm_call))) {
Harald Welte3e6376d2010-12-22 23:54:51 +010099 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
100 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
101 goto out_reject;
102 }
103 llist_add_tail(&remote->entry, &call_list);
104 remote->net = call->net;
105 remote->callref = new_callref++;
Max3332ac42018-12-17 11:17:33 +0100106 DEBUGCC(call, remote, "Creating new remote instance.\n");
Harald Welte3e6376d2010-12-22 23:54:51 +0100107
108 /* link remote call */
109 call->remote_ref = remote->callref;
110 remote->remote_ref = call->callref;
111
Harald Weltebf0a7c92012-08-24 16:48:21 +0200112 /* send call proceeding */
113 memset(&mncc, 0, sizeof(struct gsm_mncc));
114 mncc.callref = call->callref;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200115 mncc.msg_type = MNCC_CALL_PROC_REQ;
Max3332ac42018-12-17 11:17:33 +0100116 DEBUGCC(call, remote, "Accepting call.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200117 mncc_tx_to_cc(call->net, &mncc);
Harald Weltebf0a7c92012-08-24 16:48:21 +0200118
Harald Welte3e6376d2010-12-22 23:54:51 +0100119 /* modify mode */
120 memset(&mncc, 0, sizeof(struct gsm_mncc));
121 mncc.callref = call->callref;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200122 mncc.msg_type = MNCC_LCHAN_MODIFY;
Max3332ac42018-12-17 11:17:33 +0100123 DEBUGCC(call, remote, "Modify channel mode.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200124 mncc_tx_to_cc(call->net, &mncc);
Harald Welte3e6376d2010-12-22 23:54:51 +0100125
Harald Welte3e6376d2010-12-22 23:54:51 +0100126 /* send setup to remote */
127// setup->fields |= MNCC_F_SIGNAL;
128// setup->signal = GSM48_SIGNAL_DIALTONE;
129 setup->callref = remote->callref;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200130 setup->msg_type = MNCC_SETUP_REQ;
Max3332ac42018-12-17 11:17:33 +0100131 DEBUGCC(call, remote, "Forwarding SETUP to remote.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200132 return mncc_tx_to_cc(remote->net, setup);
Harald Welte3e6376d2010-12-22 23:54:51 +0100133
134out_reject:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200135 mncc.msg_type = MNCC_REJ_REQ;
136 mncc_tx_to_cc(call->net, &mncc);
Harald Welte3e6376d2010-12-22 23:54:51 +0100137 free_call(call);
138 return 0;
139}
140
Neels Hofmeyr52558742019-05-09 01:23:09 +0200141static int mncc_alert_ind(struct gsm_call *call,
Harald Welte3e6376d2010-12-22 23:54:51 +0100142 struct gsm_mncc *alert)
143{
144 struct gsm_call *remote;
145
146 /* send alerting to remote */
147 if (!(remote = get_call_ref(call->remote_ref)))
148 return 0;
149 alert->callref = remote->callref;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200150 alert->msg_type = MNCC_ALERT_REQ;
Max3332ac42018-12-17 11:17:33 +0100151 DEBUGCC(call, remote, "Forwarding ALERT to remote.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200152 return mncc_tx_to_cc(remote->net, alert);
Harald Welte3e6376d2010-12-22 23:54:51 +0100153}
154
Neels Hofmeyr52558742019-05-09 01:23:09 +0200155static int mncc_notify_ind(struct gsm_call *call,
Harald Welte3e6376d2010-12-22 23:54:51 +0100156 struct gsm_mncc *notify)
157{
158 struct gsm_call *remote;
159
160 /* send notify to remote */
161 if (!(remote = get_call_ref(call->remote_ref)))
162 return 0;
163 notify->callref = remote->callref;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200164 notify->msg_type = MNCC_NOTIFY_REQ;
Max3332ac42018-12-17 11:17:33 +0100165 DEBUGCC(call, remote, "Forwarding NOTIF to remote.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200166 return mncc_tx_to_cc(remote->net, notify);
Harald Welte3e6376d2010-12-22 23:54:51 +0100167}
168
Neels Hofmeyr52558742019-05-09 01:23:09 +0200169static int mncc_setup_cnf(struct gsm_call *call,
Harald Welte3e6376d2010-12-22 23:54:51 +0100170 struct gsm_mncc *connect)
171{
Neels Hofmeyrf9773b02018-11-29 23:01:58 +0100172 struct gsm_mncc connect_ack;
Harald Welte3e6376d2010-12-22 23:54:51 +0100173 struct gsm_call *remote;
Harald Welte53d51f52015-12-03 14:59:04 +0100174 struct gsm_mncc_bridge bridge = { .msg_type = MNCC_BRIDGE };
Harald Welte3e6376d2010-12-22 23:54:51 +0100175
176 /* acknowledge connect */
177 memset(&connect_ack, 0, sizeof(struct gsm_mncc));
Neels Hofmeyr52558742019-05-09 01:23:09 +0200178 connect_ack.msg_type = MNCC_SETUP_COMPL_REQ;
Harald Welte3e6376d2010-12-22 23:54:51 +0100179 connect_ack.callref = call->callref;
180 DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200181 mncc_tx_to_cc(call->net, &connect_ack);
Harald Welte3e6376d2010-12-22 23:54:51 +0100182
183 /* send connect message to remote */
184 if (!(remote = get_call_ref(call->remote_ref)))
185 return 0;
Neels Hofmeyr52558742019-05-09 01:23:09 +0200186 connect->msg_type = MNCC_SETUP_RSP;
Harald Welte3e6376d2010-12-22 23:54:51 +0100187 connect->callref = remote->callref;
Max3332ac42018-12-17 11:17:33 +0100188 DEBUGCC(call, remote, "Sending CONNECT to remote.\n");
Neels Hofmeyr52558742019-05-09 01:23:09 +0200189 mncc_tx_to_cc(remote->net, connect);
Harald Welte3e6376d2010-12-22 23:54:51 +0100190
191 /* bridge tch */
Neels Hofmeyr52558742019-05-09 01:23:09 +0200192 bridge.msg_type = MNCC_BRIDGE;
Harald Welte53d51f52015-12-03 14:59:04 +0100193 bridge.callref[0] = call->callref;
194 bridge.callref[1] = call->remote_ref;
Max3332ac42018-12-17 11:17:33 +0100195 DEBUGCC(call, remote, "Bridging with remote.\n");
Harald Welte3e6376d2010-12-22 23:54:51 +0100196
Neels Hofmeyr52558742019-05-09 01:23:09 +0200197 return mncc_tx_to_cc(call->net, &bridge);
Harald Welte3e6376d2010-12-22 23:54:51 +0100198}
199
Neels Hofmeyr52558742019-05-09 01:23:09 +0200200static int mncc_disc_ind(struct gsm_call *call,
Harald Welte3e6376d2010-12-22 23:54:51 +0100201 struct gsm_mncc *disc)
202{
203 struct gsm_call *remote;
204
205 /* send release */
206 DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n",
207 call->callref, disc->cause.value);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200208 disc->msg_type = MNCC_REL_REQ;
209 mncc_tx_to_cc(call->net, disc);
Harald Welte3e6376d2010-12-22 23:54:51 +0100210
211 /* send disc to remote */
212 if (!(remote = get_call_ref(call->remote_ref))) {
213 return 0;
214 }
Neels Hofmeyr52558742019-05-09 01:23:09 +0200215 disc->msg_type = MNCC_DISC_REQ;
Harald Welte3e6376d2010-12-22 23:54:51 +0100216 disc->callref = remote->callref;
Max3332ac42018-12-17 11:17:33 +0100217 DEBUGCC(call, remote, "Disconnecting remote with cause %d\n", disc->cause.value);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200218 return mncc_tx_to_cc(remote->net, disc);
Harald Welte3e6376d2010-12-22 23:54:51 +0100219}
220
Neels Hofmeyr52558742019-05-09 01:23:09 +0200221static int mncc_rel_ind(struct gsm_call *call, struct gsm_mncc *rel)
Harald Welte3e6376d2010-12-22 23:54:51 +0100222{
223 struct gsm_call *remote;
224
225 /* send release to remote */
226 if (!(remote = get_call_ref(call->remote_ref))) {
227 free_call(call);
228 return 0;
229 }
Holger Hans Peter Freythera61c7092011-01-27 15:05:45 +0100230
Neels Hofmeyr52558742019-05-09 01:23:09 +0200231 rel->msg_type = MNCC_REL_REQ;
Harald Welte3e6376d2010-12-22 23:54:51 +0100232 rel->callref = remote->callref;
Max3332ac42018-12-17 11:17:33 +0100233 DEBUGCC(call, remote, "Releasing remote with cause %d\n", rel->cause.value);
Harald Welte3e6376d2010-12-22 23:54:51 +0100234
Holger Hans Peter Freythera61c7092011-01-27 15:05:45 +0100235 /*
236 * Release this side of the call right now. Otherwise we end up
237 * in this method for the other call and will also try to release
238 * it and then we will end up with a double free and a crash
239 */
Harald Welte3e6376d2010-12-22 23:54:51 +0100240 free_call(call);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200241 mncc_tx_to_cc(remote->net, rel);
Harald Welte3e6376d2010-12-22 23:54:51 +0100242
243 return 0;
244}
245
Neels Hofmeyr52558742019-05-09 01:23:09 +0200246static int mncc_rel_cnf(struct gsm_call *call, struct gsm_mncc *rel)
Harald Welte3e6376d2010-12-22 23:54:51 +0100247{
248 free_call(call);
249 return 0;
250}
251
Harald Welte3e6376d2010-12-22 23:54:51 +0100252/* Internal MNCC handler input function (from CC -> MNCC -> here) */
Harald Welte29b64e92010-12-23 01:07:46 +0100253int int_mncc_recv(struct gsm_network *net, struct msgb *msg)
Harald Welte3e6376d2010-12-22 23:54:51 +0100254{
Harald Welte29b64e92010-12-23 01:07:46 +0100255 void *arg = msgb_data(msg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100256 struct gsm_mncc *data = arg;
Harald Welte29b64e92010-12-23 01:07:46 +0100257 int msg_type = data->msg_type;
Harald Welte3e6376d2010-12-22 23:54:51 +0100258 int callref;
259 struct gsm_call *call = NULL, *callt;
260 int rc = 0;
261
Harald Welte3e6376d2010-12-22 23:54:51 +0100262 /* find callref */
263 callref = data->callref;
264 llist_for_each_entry(callt, &call_list, entry) {
265 if (callt->callref == callref) {
266 call = callt;
267 break;
268 }
269 }
270
271 /* create callref, if setup is received */
272 if (!call) {
273 if (msg_type != MNCC_SETUP_IND)
Harald Welte29b64e92010-12-23 01:07:46 +0100274 goto out_free; /* drop */
Harald Welte3e6376d2010-12-22 23:54:51 +0100275 /* create call */
276 if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) {
277 struct gsm_mncc rel;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100278
Harald Welte3e6376d2010-12-22 23:54:51 +0100279 memset(&rel, 0, sizeof(struct gsm_mncc));
Neels Hofmeyr52558742019-05-09 01:23:09 +0200280 rel.msg_type = MNCC_REL_REQ;
Harald Welte3e6376d2010-12-22 23:54:51 +0100281 rel.callref = callref;
282 mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
283 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200284 mncc_tx_to_cc(net, &rel);
Harald Welte29b64e92010-12-23 01:07:46 +0100285 goto out_free;
Harald Welte3e6376d2010-12-22 23:54:51 +0100286 }
287 llist_add_tail(&call->entry, &call_list);
288 call->net = net;
289 call->callref = callref;
290 DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
291 }
292
Andreas Eversberg9acbe4c2013-03-11 08:12:43 +0100293 if (mncc_is_data_frame(msg_type)) {
Neels Hofmeyre2f24d52017-05-08 15:12:20 +0200294 LOGP(DMNCC, LOGL_ERROR, "(call %x) Received data frame, which is not supported.\n",
295 call->callref);
Andreas Eversberg9acbe4c2013-03-11 08:12:43 +0100296 goto out_free;
Harald Welte3e6376d2010-12-22 23:54:51 +0100297 }
298
Andreas Eversberg9acbe4c2013-03-11 08:12:43 +0100299 DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
300 get_mncc_name(msg_type));
301
Vadim Yanitskiy880a6932020-08-22 17:47:54 +0700302 switch (msg_type) {
Harald Welte3e6376d2010-12-22 23:54:51 +0100303 case MNCC_SETUP_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200304 rc = mncc_setup_ind(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100305 break;
306 case MNCC_SETUP_CNF:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200307 rc = mncc_setup_cnf(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100308 break;
309 case MNCC_SETUP_COMPL_IND:
310 break;
311 case MNCC_CALL_CONF_IND:
312 /* we now need to MODIFY the channel */
Neels Hofmeyr52558742019-05-09 01:23:09 +0200313 data->msg_type = MNCC_LCHAN_MODIFY;
314 mncc_tx_to_cc(call->net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100315 break;
316 case MNCC_ALERT_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200317 rc = mncc_alert_ind(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100318 break;
319 case MNCC_NOTIFY_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200320 rc = mncc_notify_ind(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100321 break;
322 case MNCC_DISC_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200323 rc = mncc_disc_ind(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100324 break;
325 case MNCC_REL_IND:
326 case MNCC_REJ_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200327 rc = mncc_rel_ind(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100328 break;
329 case MNCC_REL_CNF:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200330 rc = mncc_rel_cnf(call, arg);
Harald Welte3e6376d2010-12-22 23:54:51 +0100331 break;
332 case MNCC_FACILITY_IND:
333 break;
334 case MNCC_START_DTMF_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200335 data->msg_type = MNCC_START_DTMF_REJ;
336 rc = mncc_tx_to_cc(net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100337 break;
338 case MNCC_STOP_DTMF_IND:
Neels Hofmeyr52558742019-05-09 01:23:09 +0200339 data->msg_type = MNCC_STOP_DTMF_RSP;
340 rc = mncc_tx_to_cc(net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100341 break;
342 case MNCC_MODIFY_IND:
343 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
344 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
345 DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n",
346 call->callref, data->cause.value);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200347 data->msg_type = MNCC_MODIFY_REJ;
348 rc = mncc_tx_to_cc(net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100349 break;
350 case MNCC_MODIFY_CNF:
351 break;
352 case MNCC_HOLD_IND:
353 mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU,
354 GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
355 DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n",
356 call->callref, data->cause.value);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200357 data->msg_type = MNCC_HOLD_REJ;
358 rc = mncc_tx_to_cc(net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100359 break;
360 case MNCC_RETRIEVE_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 RETRIEVE with cause %d\n",
364 call->callref, data->cause.value);
Neels Hofmeyr52558742019-05-09 01:23:09 +0200365 data->msg_type = MNCC_RETRIEVE_REJ;
366 rc = mncc_tx_to_cc(net, data);
Harald Welte3e6376d2010-12-22 23:54:51 +0100367 break;
Harald Welte3e6376d2010-12-22 23:54:51 +0100368 default:
Vadim Yanitskiy4d755592020-08-22 17:49:35 +0700369 LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message '%s' unhandled\n",
370 callref, get_mncc_name(msg_type));
Harald Welte3e6376d2010-12-22 23:54:51 +0100371 break;
372 }
373
Harald Welte29b64e92010-12-23 01:07:46 +0100374out_free:
Holger Hans Peter Freythera8ddb082012-03-01 20:30:32 +0100375 msgb_free(msg);
Harald Welte29b64e92010-12-23 01:07:46 +0100376
Harald Welte3e6376d2010-12-22 23:54:51 +0100377 return rc;
378}